Persistently write to a file from windows kernel driver - windows

Hi I'm new to kernel level programming and trying to build a simple log writing driver. What I'm trying to achieve is to have a persistent driver will write referred text every predefined interval to a file in system path. (I'm not familiar IRQ hooking yet)
I have following globals for timing
// Timer
PKTIMER pTimer = NULL; // Pointer to the timer
PKDPC pDpcObject = NULL; // Pointer to the DPC
#define IDLE_INTERVAL (10000)
I call following code in the DriverEntry (However, problem with following code is its writing feature fails when computer restarted) Could someone suggest a fix ? Should it be called by IRQ Major call ?
while(1)
{
if (pTimer == NULL) // if timer object does not exist:
{
// Allocate memory for the object timer
pTimer = (PKTIMER) ExAllocatePool (NonPagedPool, sizeof (KTIMER));
KeInitializeTimer (pTimer); // Initialize the timer object
// Allocate memory for the DPC object and initialize it
pDpcObject = (PKDPC) ExAllocatePool (NonPagedPool, sizeof (KDPC));
KeInitializeDpc (pDpcObject, MyDeferredRoutine, pTimer);
}
LARGE_INTEGER dueTime;
dueTime.QuadPart = -10000 * IDLE_INTERVAL; // 10000 * 10000 * 1 ns
// "Platoon" timer:
KeSetTimerEx (pTimer,
dueTime, // latency relative interval
(IDLE_INTERVAL / 2), // period of 5 seconds, i.e. 5000 * 1 ms
pDpcObject);
if (KeReadStateTimer (pTimer))
{
//DbgPrint ("- Example- KeReadStateTimer returns TRUE.");
}
else
{
// DbgPrint ("- Example- KeReadStateTimer returns FALSE.");
}
}
Status = KeWaitForSingleObject (pTimer,
Executive, // IN KWAIT_REASON WaitReason,
KernelMode, // IN KPROCESSOR_MODE WaitMode,
FALSE, // IN BOOLEAN Alertable,
NULL); // IN PLARGE_INTEGER Timeout OPTIONAL
RtlInitUnicodeString(&TestName, L"\\??\\C:\\log.txt");
InitializeObjectAttributes(&ObjAttr, &TestName,
OBJ_CASE_INSENSITIVE,
0, NULL);
Status = NtCreateFile(&TestFile,
FILE_WRITE_DATA + SYNCHRONIZE,
&ObjAttr,
&IoStatus, NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_WRITE,
FILE_OVERWRITE_IF,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL, 0);
if(Status == STATUS_SUCCESS)
{
Status = NtWriteFile(TestFile,
0, NULL, NULL,
&IoStatus,
(PCHAR)"OUR LOG STORED TO LOG FILE",
22,
NULL, NULL);
}
NtClose(TestFile);
}

Related

minifilter send message to r3

I'm writing a minifilter, which wants to notify the r3 application to popup a messagebox in some cases. I used fltsendmessage in minifilter and filtergetmessage in r3. In r3 application, I wrote like:
while (INVALID_HANDLE_VALUE == s_portWorker.m_clientPort)
{
hResult = FilterConnectCommunicationPort(SERVER_PORTNAME_POPUP, 0, NULL, 0, NULL, &s_portWorker.m_clientPort);
if (IS_ERROR(hResult)) {
Sleep(1000);
}
while (true)
{
ZeroMemory(&getStruct, sizeof(GET_STRUCT));
hResult = FilterGetMessage(s_portWorker.m_clientPort, (PFILTER_MESSAGE_HEADER)&getStruct, sizeof(GET_STRUCT), NULL);
}
}
It works fine. But when I stop my minifilter, calling FltCloseCommunicationPort() in driver unload. The port has been closed, but the connection is still in, my r3 process will blocks on FilterGetMessage and never return.
I want to stop waiting the messagew when port close, and try to reconnect to my minifilter. What should I do? Since that FilterGetMessage() routine doesn't support a timeout mechanism, Do I have to create a event to notify the r3 when stop the filter?
You can implement a timeout mechanism by using lpOverlapped parameter.
HANDLE hWait = CreateEvent(NULL, TRUE, FALSE, NULL);
OVERLAPPED op = {0};
op.hEvent = hWait;
HRESULT hResult = FilterGetMessage(s_portWorker.m_clientPort, (PFILTER_MESSAGE_HEADER)&getStruct, sizeof(GET_STRUCT), &op);
if (hResult == HRESULT_FROM_WIN32(ERROR_IO_PENDING))
{
HANDLE phHandles[2] = { hWait, g_hTerm };
WaitForMultipleObjects(2, phHandles, TIME_OUT_VALUE);
}
And you can stop listenning by calling SetEvent(g_hTerm);

Blue screen when rewriting packets at DATAGRAM_DATA layer in WFP

I've been trying to modify outgoing DNS packets via the DATAGRAM_DATA layer in WFP, however i get blue screen errors when rewriting the destination ip in the outgoing packet. What am i doing wrong?
I admit i found the parameters for FwpsInjectTransportSendAsync a bit confusing, and was unsure exactly what to put in for the sendParams arg - though i think what i have looks right.
RtlIpv4StringToAddressExW(
L"1.1.1.1", // hard-coding the new (rewritten) dns server for now
FALSE,
&sin4.sin_addr,
&sin4.sin_port);
RtlIpv4StringToAddressExW(
L"8.8.8.8", // hard-coding the original dns server for now
FALSE,
&origSin4.sin_addr,
&origSin4.sin_port);
if ((Direction == FWP_DIRECTION_OUTBOUND) && (PacketInjectionState == FWPS_PACKET_NOT_INJECTED) && (RemotePort == 53) && (RemoteAddress == origSin4.sin_addr.S_un.S_addr))
{
UINT32 IpHeaderSize = inMetaValues->ipHeaderSize;
UINT32 TransportHeaderSize = inMetaValues->transportHeaderSize;
UINT64 endpointHandle = inMetaValues->transportEndpointHandle;
PNET_BUFFER NetBuffer = NET_BUFFER_LIST_FIRST_NB((PNET_BUFFER_LIST)layerData);
NdisRetreatNetBufferDataStart(NetBuffer, IpHeaderSize + TransportHeaderSize, 0, NULL);
PNET_BUFFER_LIST NetBufferList = NULL;
NTSTATUS Status = FwpsAllocateCloneNetBufferList(layerData, NULL, NULL, 0, &NetBufferList);
if (!NT_SUCCESS(Status))
{
return;
}
NdisAdvanceNetBufferDataStart(NetBuffer, IpHeaderSize + TransportHeaderSize, FALSE, NULL);
if (!NetBufferList)
{
return;
}
NetBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList);
PIPV4_HEADER IpHeader = NdisGetDataBuffer(NetBuffer, sizeof(IPV4_HEADER), NULL, 1, 0);
// Rewriting the dest ip
IpHeader->DestinationAddress = sin4.sin_addr.S_un.S_addr;
// Updating the IP checksum
UpdateIpv4HeaderChecksum(IpHeader, sizeof(IPV4_HEADER));
// not 100% sure the sendParams argument is setup correctly, the docs are slightly unclear
FWPS_TRANSPORT_SEND_PARAMS sendParams = {
.remoteAddress = (UCHAR*)IpHeader->DestinationAddress,
.remoteScopeId = inMetaValues->remoteScopeId,
.controlData = inMetaValues->controlData,
.controlDataLength = inMetaValues->controlDataLength,
.headerIncludeHeader = inMetaValues->headerIncludeHeader,
.headerIncludeHeaderLength = inMetaValues->headerIncludeHeaderLength
};
Status = FwpsInjectTransportSendAsync(g_InjectionHandle, NULL, endpointHandle, 0, &sendParams, AF_INET, inMetaValues->compartmentId, NetBufferList, DriverDatagramDataInjectComplete, NULL);
if (!NT_SUCCESS(Status))
{
FwpsFreeCloneNetBufferList(NetBufferList, 0);
}
classifyOut->actionType = FWP_ACTION_BLOCK;
classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
}
Two things stand out to me, both in the sendParams.
First, remoteAddress is incorrect. It needs to a pointer to the address, so it should be (UCHAR*)&IpHeader->DestinationAddress.
Second, FwpsInjectTransportSendAsync() is asynchronous so any parameters you pass to it need to stay valid until it completes which may be after your calling function returns. Typically you allocate some context structure that contains sendParams and deep copies of relevant members (remoteAddress and controlData). You pass this as the context to the completion routine where you free it.

Who owns the WDFMEMORY in a WDFREQUEST?

I am writing a Windows kernel driver. I need to create a new I/O request and allocate my own memory for the input buffer.
// Create request
WDFREQUEST request;
status = WdfRequestCreate(WDF_NO_OBJECT_ATTRIBUTES, target, &request);
if (!NT_SUCCESS(status)) {
goto exit;
}
// Allocate buffer for request
WDFMEMORY inputMemory;
status = WdfMemoryCreate(WDF_NO_OBJECT_ATTRIBUTES, PagedPool, 0, 1024, &inputMemory, NULL);
if (!NT_SUCCESS(status)) {
goto exit;
}
// Assign input buffer to request
status = WdfIoTargetFormatRequestForIoctl(target, request, IOCTL_FOO, inputMemory, NULL, NULL, NULL);
if (!NT_SUCCESS(status)) {
goto exit;
}
// Asynchronously send the ioctl request
WdfRequestSetCompletionRoutine(request, MyCompletionRoutine, NULL);
if (!WdfRequestSend(request, target, NULL)) {
status = WdfRequestGetStatus(request);
goto exit;
}
My question is, if WdfIoTargetFormatRequestForIoctl completes successfully, should I also perform WdfObjectDelete(inputMemory) in my cleanup, or will WdfObjectDelete(request) destroy both the memory and the request? Also, is the answer the same for both the error cleanup within the function and in the completion routine?
According to this the Driver object owns the memory, it will only be cleanup when you unloaded the driver.
if you can done with with the memory you should call WdfObjectDelete() to be not keep unused memory.

Create multiple ramdisk KMDF

I have a question about the KMDF Ramdisk sample of Microsoft.
How do create more than one ramdisk device? How will the PnP manager know to call EvtDeviceAdd and can I control how many times it is called?
WdfDeviceCreate() is used to create ramdisk device in RamDiskEvtDeviceAdd(), but I can not install more than one instance of this ramdisk driver.
NTSTATUS
RamDiskEvtDeviceAdd(
IN WDFDRIVER Driver,
IN PWDFDEVICE_INIT DeviceInit
) {
WDF_OBJECT_ATTRIBUTES deviceAttributes;
NTSTATUS status;
WDFDEVICE device;
WDF_OBJECT_ATTRIBUTES queueAttributes;
WDF_IO_QUEUE_CONFIG ioQueueConfig;
PDEVICE_EXTENSION pDeviceExtension;
PQUEUE_EXTENSION pQueueContext = NULL;
WDFQUEUE queue;
DECLARE_CONST_UNICODE_STRING(ntDeviceName, NT_DEVICE_NAME);
DECLARE_CONST_UNICODE_STRING(MY_SDDL_STRING, L"D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;WD)(A;;GA;;;RC)");
PAGED_CODE();
UNREFERENCED_PARAMETER(Driver);
// Set name
status = WdfDeviceInitAssignName(DeviceInit, &ntDeviceName);
if (!NT_SUCCESS(status)) {
return status;
}
// Set permission
status = WdfDeviceInitAssignSDDLString(DeviceInit, &MY_SDDL_STRING);
if (!NT_SUCCESS(status)) {
return status;
}
WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_DISK);
WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect);
WdfDeviceInitSetExclusive(DeviceInit, FALSE);
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_EXTENSION);
deviceAttributes.EvtCleanupCallback = RamDiskEvtDeviceContextCleanup;
status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
if (!NT_SUCCESS(status)) {
return status;
}
pDeviceExtension = DeviceGetExtension(device);
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE (
&ioQueueConfig,
WdfIoQueueDispatchSequential
);
ioQueueConfig.EvtIoDeviceControl = RamDiskEvtIoDeviceControl;
ioQueueConfig.EvtIoRead = RamDiskEvtIoRead;
ioQueueConfig.EvtIoWrite = RamDiskEvtIoWrite;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&queueAttributes, QUEUE_EXTENSION);
__analysis_assume(ioQueueConfig.EvtIoStop != 0);
status = WdfIoQueueCreate( device,
&ioQueueConfig,
&queueAttributes,
&queue );
__analysis_assume(ioQueueConfig.EvtIoStop == 0);
if (!NT_SUCCESS(status)) {
return status;
}
// Context is the Queue handle
pQueueContext = QueueGetExtension(queue);
// Set the context for our default queue as our device extension.
pQueueContext->DeviceExtension = pDeviceExtension;
// Now do any RAM-Disk specific initialization
pDeviceExtension->DiskRegInfo.DriveLetter.Buffer =
(PWSTR) &pDeviceExtension->DriveLetterBuffer;
pDeviceExtension->DiskRegInfo.DriveLetter.MaximumLength =
sizeof(pDeviceExtension->DriveLetterBuffer);
// Get the disk parameters from the registry
RamDiskQueryDiskRegParameters(
WdfDriverGetRegistryPath(WdfDeviceGetDriver(device)),
&pDeviceExtension->DiskRegInfo
);
// Allocate memory for the disk image.
pDeviceExtension->DiskImage = ExAllocatePoolWithTag(
NonPagedPool,
pDeviceExtension->DiskRegInfo.DiskSize,
RAMDISK_TAG
);
if (pDeviceExtension->DiskImage) {
UNICODE_STRING deviceName;
UNICODE_STRING win32Name;
RamDiskFormatDisk(pDeviceExtension);
status = STATUS_SUCCESS;
// Now try to create a symbolic link for the drive letter.
RtlInitUnicodeString(&win32Name, DOS_DEVICE_NAME);
RtlInitUnicodeString(&deviceName, NT_DEVICE_NAME);
pDeviceExtension->SymbolicLink.Buffer = (PWSTR)
&pDeviceExtension->DosDeviceNameBuffer;
pDeviceExtension->SymbolicLink.MaximumLength =
sizeof(pDeviceExtension->DosDeviceNameBuffer);
pDeviceExtension->SymbolicLink.Length = win32Name.Length;
RtlCopyUnicodeString(&pDeviceExtension->SymbolicLink, &win32Name);
RtlAppendUnicodeStringToString(&pDeviceExtension->SymbolicLink,
&pDeviceExtension->DiskRegInfo.DriveLetter);
status = WdfDeviceCreateSymbolicLink(device,
&pDeviceExtension->SymbolicLink);
}
return status;
Please help me! Thanks
I understand this is an old post, but since there are no useful answers, I thought I might add my thoughts.
EvtDeviceAdd is called by the KMDF framework when a device matches a hardware ID that your INF supports. (https://msdn.microsoft.com/en-us/library/windows/hardware/ff541693%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396).
According to the VirtualVolume documentation (https://code.msdn.microsoft.com/windowshardware/VirtualVolume-83334efd), the only way to create a device is to call devcon install virtvol.inf virtvol.
You can make multiple RAMDisks by calling devcon install multiple times.

PAGE_FAULT_IN_NONPAGED_AREA error at KeWaitForSingleObject function

Can this codes cause a problem?(PAGE_FAULT_IN_NONPAGED_AREA)
KEVENT waitEvent; //allocate on stack
LARGE_INTEGER timeout;
KeInitializeEvent(&waitEvent, NotificationEvent, FALSE);
KeResetEvent(&waitEvent);
timeout.QuadPart = -(100 * 10000); // 100 ms
while(pDataChannel->useCount)
{
KeWaitForSingleObject(&waitEvent, Executive, KernelMode, FALSE, &timeout);
}
Can the waitEvent valiable be paged-out?
Is the variable must allocated on a non-paged pool?
Is pDataChannel valid? Or are you running at DISPATCH_LEVEL?
These can lead to the error you have.

Resources