Hello everyone, I have been trying to add an IOCTL backdoor handle in SimpleAudioSample to create a virtual mic driver which will let me stream audio into the virtual microphone device from a user-mode app (Unity), instead of the default sine tone playing in the sample.
Due to my lack of experience with windows drivers, I'm unable to fully grasp the architechture and the best place to add my IOCTL handle without intercepting Portcls and causing a BSOD.
I tried adding my IOCTL handler in AddDriver and even DeviceEntry, but doing so in either cases, I'm ABLE to receive the input audio buffers BUT it breaks device enumeration on system settings, so I can no longer then see the device.
The way I was attempting to add IOCTL handle in AddDriver was this -
if (NT_SUCCESS(status)) {
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = CMiniportWaveRTStream::HandleCustomIOCTL;
PhysicalDeviceObject->Flags |= DO_BUFFERED_IO;
PhysicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
}
NTSTATUS
CMiniportWaveRTStream::HandleCustomIOCTL
(
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp
)
/*++
Routine Description:
Add a custom IOCTL handler that works with the Ring buffer.
--*/
{
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status = STATUS_SUCCESS;
ULONG_PTR bytesTransferred = 0;
DbgPrint("SIMPLEAUDIOSAMPLE: HandleCustomIOCTL Entry\n");
DbgPrint("SIMPLEAUDIOSAMPLE: Received IOCTL Code: 0x%x\n",
irpStack->Parameters.DeviceIoControl.IoControlCode);
// Log all buffer parameters
DbgPrint("SIMPLEAUDIOSAMPLE: Input Buffer Length: %d\n",
irpStack->Parameters.DeviceIoControl.InputBufferLength);
DbgPrint("SIMPLEAUDIOSAMPLE: Output Buffer Length: %d\n",
irpStack->Parameters.DeviceIoControl.OutputBufferLength);
DbgPrint("SIMPLEAUDIOSAMPLE: HandleCustomIOCTL - Code: 0x%x, Method: %d\n",
irpStack->Parameters.DeviceIoControl.IoControlCode,
irpStack->Parameters.DeviceIoControl.IoControlCode & 3);
// Get the buffered data
PVOID buffer = Irp->AssociatedIrp.SystemBuffer;
ULONG inLength = irpStack->Parameters.DeviceIoControl.InputBufferLength;
DbgPrint("SIMPLEAUDIOSAMPLE: Buffer: %p, Length: %d\n", buffer, inLength);
if (buffer && inLength > 0) {
SHORT* samples = (SHORT*)buffer;
DbgPrint("SIMPLEAUDIOSAMPLE: First sample: %d\n", samples[0]);
}
switch (irpStack->Parameters.DeviceIoControl.IoControlCode)
{
case 0x2f0007: // Audio write attempt
{
PVOID inputBuffer = Irp->AssociatedIrp.SystemBuffer;
ULONG inputBufferLength = irpStack->Parameters.DeviceIoControl.InputBufferLength;
DbgPrint("SIMPLEAUDIOSAMPLE: Write request - Buffer: %p, Length: %d\n",
inputBuffer, inputBufferLength);
if (!inputBuffer || inputBufferLength == 0) {
DbgPrint("SIMPLEAUDIOSAMPLE: Empty buffer received\n");
status = STATUS_INVALID_PARAMETER;
break;
}
// Log first few samples
SHORT* samples = (SHORT*)inputBuffer;
DbgPrint("SIMPLEAUDIOSAMPLE: First 5 samples: %d %d %d %d %d\n",
samples[0], samples[1], samples[2], samples[3], samples[4]);
PortClassDeviceContext* deviceContext = (PortClassDeviceContext*)DeviceObject->DeviceExtension;
if (!deviceContext || !deviceContext->m_pCommon) {
DbgPrint("SIMPLEAUDIOSAMPLE: Invalid device context\n");
status = STATUS_INVALID_DEVICE_STATE;
break;
}
CMiniportWaveRT* miniport = (CMiniportWaveRT*)deviceContext->m_pCommon;
PCMiniportWaveRTStream stream = miniport->GetActiveCaptureStream();
if (!stream) {
DbgPrint("SIMPLEAUDIOSAMPLE: No active stream\n");
status = STATUS_INVALID_DEVICE_STATE;
break;
}
if (!stream->m_RingBuffer) {
DbgPrint("SIMPLEAUDIOSAMPLE: No ring buffer\n");
status = STATUS_INVALID_DEVICE_STATE;
break;
}
status = stream->m_RingBuffer->Write(
(PBYTE)inputBuffer,
inputBufferLength);
if (NT_SUCCESS(status)) {
bytesTransferred = inputBufferLength;
DbgPrint("SIMPLEAUDIOSAMPLE: Successfully wrote %d bytes\n", inputBufferLength);
}
else {
DbgPrint("SIMPLEAUDIOSAMPLE: Write failed, status = 0x%x\n", status);
}
stream->Release();
break;
}
case 0x2f0003: // Standard audio query
DbgPrint("SIMPLEAUDIOSAMPLE: Standard query received\n");
status = STATUS_SUCCESS;
break;
default:
DbgPrint("SIMPLEAUDIOSAMPLE: Unknown IOCTL code: 0x%x\n",
irpStack->Parameters.DeviceIoControl.IoControlCode);
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = bytesTransferred;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
This is after PcAddAdapterDevice call succeeds. This to my best guess rerouted all Portcls IOCTL codes into this handle and caused enumeration in system to fail. If I do this in DriverEntry instead of AddDevice, it still makes device listing fail.
Interestingly, I see continuous logs printed from HandleCustomIOCTL even if device enumeration fails, in either case.
I'm really lost on where to actually add in athe IOCTL backdoor in question. A similar post describes the same problem but the IOCTL part is skimmed over, so I'm not sure what was done there.
I just need directions on where to go from here regarding adding the IOCTL handle without disturbing Portcls's enumeration flow.
I do have a ring buffer class ready for IOCTL. Any help will be greatly appreciated! Again, sorry if the question is very basic.