"FltStartFiltering has not been called" Error of trying to attach a volume in DriverEntry - winapi

I have tried to attach a volume while the driver service starts, but I got "The filter is not ready for attachment to volumes because it has not finished initialize (FltStartFiltering has not been called)." and immediately I got blue screen. I have already called the FltStartFiltering but I don't know why it didn't work.
Below is my code:
status = FltRegisterFilter(DriverObject,
&FilterRegistration,
&MiniSpyData.Filter);
if (!NT_SUCCESS(status)) {
leave;
}
status = FltBuildDefaultSecurityDescriptor(&sd,
FLT_PORT_ALL_ACCESS);
if (!NT_SUCCESS(status)) {
leave;
}
RtlInitUnicodeString(&uniString, WOODY_PORT_NAME);
InitializeObjectAttributes(&oa,
&uniString,
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
NULL,
sd);
status = FltCreateCommunicationPort(MiniSpyData.Filter,
&MiniSpyData.ServerPort,
&oa,
NULL,
SpyConnect,
SpyDisconnect,
SpyMessage,
1);
FltFreeSecurityDescriptor(sd);
if (!NT_SUCCESS(status)) {
leave;
}
//
// We are now ready to start filtering
//
status = FltStartFiltering(MiniSpyData.Filter);
if (!NT_SUCCESS(status)) {
FltUnregisterFilter(MiniSpyData.Filter);
}
else {
//Here is what I want to attach
RtlInitUnicodeString(&uniString, L"\\Device\\HarddiskVolume1");
PFLT_VOLUME vol;
FltGetVolumeFromName(&MiniSpyData.Filter, &uniString, &vol);
status = FltAttachVolume(&MiniSpyData.Filter, vol, NULL, NULL);
}

RtlInitUnicodeString(&uniString, L"\\Device\\HarddiskVolume1");
PFLT_VOLUME vol;
FltGetVolumeFromName(&MiniSpyData.Filter, &uniString, &vol);
status = FltAttachVolume(&MiniSpyData.Filter, vol, NULL, NULL);
The part above is simply not needed and wrong as well.
Let me explain:
The FltGetVolumeFromName routine take a PFLT_FILTER as the first parameter and from what I see in your code you are giving it a PFLT_FILTER*
You don't need to manually attach to volumes since you will automatically attach and be called in your instance context unless you set the FLTFL_INSTANCE_SETUP_MANUAL_ATTACHMENT in your instance context registration flags. See this for more details.
You don't want to do this during boot because the volume might not be there yet and thus your potential BSOD.
Good luck.

Related

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.

Get windows session's user name in c++

I am trying to understand better how Windows sessions (TS sessions and log on sessions) works (currently in XP), so maybe my whole question or what I am trying to do is impossible.
I am running a Windows service (in XP), which runs in session 0, and I am trying to get the username attached to this session using WTSQueryUserToken().
Now, in session 0 there are several usernames: SYSTEM, theuser (logged on user),NETWORK SERVICE, LOCAL SERVICE.
When I use WTSQueryUserToken() I get "theuser" (which is the Active session), but I am trying to get the username of my service (which is SYSTEM).
Is that possible or did I simply get it all wrong?
I use the following code to get user token for my process
HANDLE GetProcessOwnerToken(DWORD pid)
{
if (!pid) return NULL;
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (!hProcess) return NULL;
HANDLE hToken = NULL;
if(OpenProcessToken(hProcess, MAXIMUM_ALLOWED, &hToken))
{
HANDLE result = INVALID_HANDLE_VALUE;
if(DuplicateTokenEx(hToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &result))
{
if(result != INVALID_HANDLE_VALUE)
{
CloseHandle(hToken);
CloseHandle(hProcess);
return result;
}
}
CloseHandle(hToken);
}
CloseHandle(hProcess);
return NULL;
}
I have no idea if it works for services as well, I think it should.

Multithreaded IOCP Client Issue

I am writing a multithreaded client that uses an IO Completion Port.
I create and connect the socket that has the WSA_FLAG_OVERLAPPED attribute set.
if ((m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
throw std::exception("Failed to create socket.");
}
if (WSAConnectByName(m_socket, L"server.com", L"80", &localAddressLength, reinterpret_cast<sockaddr*>(&localAddress), &remoteAddressLength, &remoteAddress, NULL, NULL) == FALSE)
{
throw std::exception("Failed to connect.");
}
I associate the IO Completion Port with the socket.
if ((m_hIOCP = CreateIoCompletionPort(reinterpret_cast<HANDLE>(m_socket), m_hIOCP, NULL, 8)) == NULL)
{
throw std::exception("Failed to create IOCP object.");
}
All appears to go well until I try to send some data over the socket.
SocketData* socketData = new SocketData;
socketData->hEvent = 0;
DWORD bytesSent = 0;
if (WSASend(m_socket, socketData->SetBuffer(socketData->GenerateLoginRequestHeader()), 1, &bytesSent, NULL, reinterpret_cast<OVERLAPPED*>(socketData), NULL) == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING)
{
throw std::exception("Failed to send data.");
}
Instead of returning SOCKET_ERROR with the last error set to WSA_IO_PENDING, WSASend returns immediately.
I need the IO to pend and for it's completion to be handled in my thread function which is also my worker thread.
unsigned int __stdcall MyClass::WorkerThread(void* lpThis)
{
}
I've done this before but I don't know what is going wrong in this case, I'd greatly appreciate any efforts in helping me fix this problem.
It's not a problem unless you make it so.
As long as you're not calling SetFileCompletionNotificationModes() and setting the flag to skip completion port processing on success then even if WSARecv (or whatever) returns SUCCESS an IO Completion Packet is queued to the IOCP the same as if ERROR_IO_PENDING was returned. Thus you need no special handling for the non error return case.
See http://support.microsoft.com/default.aspx?scid=kb;en-us;Q192800 for details.
First of all break the call into more clear logic:
int nRet = WSASend(m_socket, socketData->SetBuffer(socketData->GenerateLoginRequestHeader()), 1, NULL, NULL, reinterpret_cast<OVERLAPPED*>(socketData), NULL);
if (nRet == SOCKET_ERROR)
{
if ((WSAGetLastError()) == WSA_IO_PENDING)
nRet = 0; // ok
else
throw std::exception("Failed to send data."); // failed
}
Also, as you can see in my code, you should NOT pass the "&bytesSent" parameter according to WSASend:
Use NULL for this parameter if the
lpOverlapped parameter is not NULL to
avoid potentially erroneous results.
Besides that your call to WSASend() looks fine.

Resources