[wdmaudiodev] Interacting with MSVAD through IOCTL

  • From: plznospam@xxxxxxxxx
  • To: wdmaudiodev@xxxxxxxxxxxxx
  • Date: Tue, 07 May 2019 19:10:35 +0200

Hello everyone!

This list and your efforts here are a great resource.
I'm a fairly experienced software engineer, but my knowledge in regards to OS (and in particular Windows) programming is limited.
Hopefully there's someone out there that can lend a helping hand in my endeavor.

The ultimate goal is as follows:
Have bi-directional stream exchange between the raw operating system audio and JACK (jackaudio.org).
In other words, the audio samples rendered through Windows should be available in a user-space application instead of being sent to a DAC, and the samples recorded from the microphone should be read from user-space memory instead of from an ADC.
To achieve this, I am trying my luck with MSVAD.

Since JACK (a really potent audio framework) is used, it would be beneficial to have very little latency between both systems.
My initial approach is thus to modify the SaveData part of MSVAD and share a fixed-size buffer between the driver and the JACK user-space application.
Now from most research it seems that this ultimately will require IOCTL structures to break the boundary between kernel and user space (in a way that is regarded as sane and not a massive security hole).
As additional reference, I found the NT Insider sample about IOCTL (www.osronline.com/article.cfm%5Earticle=39.htm) really helpful.
In another thread on this list (www.freelists.org/post/wdmaudiodev/for-help-in-implementation-virtual-audio-driver), there is a similar approach with a little code to start with.
I combined both of these resources with the MS samples about IOCTL (github.com/microsoft/Windows-driver-samples/blob/master/general/ioctl/wdm/sys/sioctl.c) and ended up with most of the required boilerplate for the driver (which is at the very bottom). But it fails. Installing the driver is no problem as is evident by the kernel debug traces. As soon as an application tries to create a handle to the driver with CreateFile() or as soon as you manually inspect its properties with WinObj, you get a fatal system error (STATUS_ACCESS_VIOLATION):

SYSTEM_SERVICE_EXCEPTION (3b)
EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.
STACK_TEXT:
fffff882`bff483e0 fffff800`3742afeb : ffffd409`00000010 fffff882`bff48458 ffffd409`000000d8 fffff882`bff48450 : portcls!AcquireRemoveLock+0x4
fffff882`bff48420 fffff807`042bc189 : ffffd409`71eef410 fffff882`bff48750 00000000`00000025 ffffd409`7da3c2a0 : portcls!DispatchCreate+0x2b
fffff882`bff48450 fffff807`042631f4 : 00000000`00000000 00000000`00000025 00000000`00000000 fffff807`04262663 : nt!IofCallDriver+0x59
fffff882`bff48490 fffff807`047331a2 : fffff882`bff48750 ffffd409`71eef410 00000000`00000025 ffffd409`7da3c2a0 : nt!IoCallDriverWithTracing+0x34
fffff882`bff484e0 fffff807`047aa029 : ffffd409`7da3c2a0 ffffd409`7da3c200 ffffd409`73e3a870 00000000`00000001 : nt!IopParseDevice+0x632
fffff882`bff48650 fffff807`047a862f : ffffd409`73e3a800 fffff882`bff488b8 00000000`00000040 ffffd409`71f35bc0 : nt!ObpLookupObjectName+0x719
fffff882`bff48820 fffff807`0470e874 : 00000000`00000001 00000000`00000000 00000000`00000000 00000000`00000028 : nt!ObOpenObjectByNameEx+0x1df
fffff882`bff48960 fffff807`0470e459 : 00000000`0014fd40 00000000`00000000 00000000`0014fd98 00000000`0014fd58 : nt!IopCreateFile+0x404
fffff882`bff48a00 fffff807`043c3285 : 00000000`00000000 00000000`00000000 00000000`00000000 fffff807`047375df : nt!NtCreateFile+0x79
fffff882`bff48a90 00007ff9`363df034 : 00007ff9`3311fb26 00000000`00000000 00000000`c0000000 ffffffff`fffffffe : nt!KiSystemServiceCopyEnd+0x25
00000000`0014fcc8 00007ff9`3311fb26 : 00000000`00000000 00000000`c0000000 ffffffff`fffffffe 00000000`00000000 : ntdll!NtCreateFile+0x14
00000000`0014fcd0 00007ff9`3311f816 : 00000000`0000000a 00000000`00000000 00000001`40043ff4 00000000`00568b20 : KERNELBASE!CreateFileInternal+0x2f6
00000000`0014fe40 00000001`40001185 : 00007ff9`327eb510 00007ff9`00000001 00000001`400454b0 00000001`40034469 : KERNELBASE!CreateFileW+0x66
00000000`0014fea0 00007ff9`327eb510 : 00007ff9`00000001 00000001`400454b0 00000001`40034469 00000000`00000003 : BridgeDaemon+0x1185
00000000`0014fea8 00007ff9`00000001 : 00000001`400454b0 00000001`40034469 00000000`00000003 00000000`40000000 : ucrtbase!iob+0xb0
00000000`0014feb0 00000001`400454b0 : 00000001`40034469 00000000`00000003 00000000`40000000 00000000`00000000 : 0x00007ff9`00000001
00000000`0014feb8 00000001`40034469 : 00000000`00000003 00000000`40000000 00000000`00000000 00000000`00000000 : BridgeDaemon+0x454b0
00000000`0014fec0 00000000`00000003 : 00000000`40000000 00000000`00000000 00000000`00000000 00000000`00000000 : BridgeDaemon+0x34469
00000000`0014fec8 00000000`40000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000001`40033f50 : 0x3
00000000`0014fed0 00000000`00000000 : 00000000`00000000 00000000`00000000 00000001`40033f50 00000000`00000000 : 0x40000000

I'm lost at this point. The use of dispatch routines is probably not correct, but it's not obvious to me how to do it correctly.
This approach has likely been tried numerous times before. Can you point me to any working solutions or references on how to proceed?

Best regards,
Fabian








#include <portcls.h>

#define PUT_GUIDS_HERE

#define MAX_MINIPORTS           3
#define NT_DEVICE_NAME          L"\\Device\\MSVAD"
#define DOS_DEVICE_NAME         L"\\??\\MSVAD"

#define DPF_ENTER(...)          { DbgPrint(__VA_ARGS__); DbgPrint("\n"); }


extern "C" DRIVER_INITIALIZE DriverEntry;
DRIVER_ADD_DEVICE AddDevice;
NTSTATUS StartDevice(IN PDEVICE_OBJECT, IN PIRP, IN PRESOURCELIST);
DRIVER_UNLOAD DriverUnload;
DRIVER_DISPATCH IOCTLDeviceControl;

typedef void(*fnPcDriverUnload) (PDRIVER_OBJECT);
fnPcDriverUnload g_DriverUnloadRoutine = NULL;

//=============================================================================
#pragma code_seg("INIT")
extern "C" NTSTATUS
DriverEntry(
IN  PDRIVER_OBJECT          DriverObject,
IN  PUNICODE_STRING         RegistryPathName
)
{
DPF_ENTER("[DriverEntry]");

NTSTATUS ntStatus;

ntStatus = PcInitializeAdapterDriver(
        DriverObject,
        RegistryPathName,
        (PDRIVER_ADD_DEVICE)AddDevice
);
if (!NT_SUCCESS(ntStatus)) { DPF_ENTER("PcInitializeAdapterDriver failed (%p)", ntStatus); }
else { DPF_ENTER("PcInitializeAdapterDriver succeeded."); }

DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IOCTLDeviceControl;
g_DriverUnloadRoutine = DriverObject->DriverUnload;
DriverObject->DriverUnload = DriverUnload;

return ntStatus;
} // DriverEntry
#pragma code_seg()

#pragma code_seg("PAGE")
//=============================================================================
NTSTATUS
AddDevice(
IN  PDRIVER_OBJECT          DriverObject,
IN  PDEVICE_OBJECT          PhysicalDeviceObject
)
{
DPF_ENTER("[AddDevice]");

NTSTATUS ntStatus;
UNICODE_STRING ntUnicodeString, ntWin32NameString;
PDEVICE_OBJECT deviceObject = NULL;

PAGED_CODE();

ntStatus = PcAddAdapterDevice(
        DriverObject,
        PhysicalDeviceObject,
        PCPFNSTARTDEVICE(StartDevice),
        MAX_MINIPORTS,
        0
);
if (!NT_SUCCESS(ntStatus)) { DPF_ENTER("PcAddAdapterDevice failed (%p)", ntStatus); }
else { DPF_ENTER("PcAddAdapterDevice succeeded."); }

RtlInitUnicodeString(&ntUnicodeString, NT_DEVICE_NAME);
RtlInitUnicodeString(&ntWin32NameString, DOS_DEVICE_NAME);

ntStatus = IoCreateDevice(
        DriverObject,
        0,
        &ntUnicodeString,
        FILE_DEVICE_UNKNOWN,
        0,
        FALSE,
        &deviceObject
);
if (!NT_SUCCESS(ntStatus)) { DPF_ENTER("IoCreateDevice failed (%p)", ntStatus); }
else { DPF_ENTER("IoCreateDevice succeeded."); }

ntStatus = IoCreateSymbolicLink(&ntWin32NameString, &ntUnicodeString);
if (!NT_SUCCESS(ntStatus)) { DPF_ENTER("IoCreateSymbolicLink failed (%p)", ntStatus); }
else { DPF_ENTER("IoCreateSymbolicLink succeeded."); }

deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
deviceObject->Flags |= DO_DIRECT_IO;

return ntStatus;
} // AddDevice

//=============================================================================
NTSTATUS
StartDevice(
    IN  PDEVICE_OBJECT          DeviceObject,
    IN  PIRP                    Irp,
    IN  PRESOURCELIST           ResourceList
)
{
DPF_ENTER("[StartDevice]");

    PAGED_CODE();

    UNREFERENCED_PARAMETER(DeviceObject);
    UNREFERENCED_PARAMETER(Irp);
    UNREFERENCED_PARAMETER(ResourceList);

    NTSTATUS ntStatus= STATUS_SUCCESS;
    return ntStatus;
} // StartDevice

//=============================================================================
VOID
DriverUnload
(
_In_ PDRIVER_OBJECT DriverObject
)
{
DPF_ENTER("[DriverUnload]");

PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject;
UNICODE_STRING uniWin32NameString;

PAGED_CODE();

RtlInitUnicodeString(&uniWin32NameString, DOS_DEVICE_NAME);

IoDeleteSymbolicLink(&uniWin32NameString);

if (deviceObject != NULL)
{
        IoDeleteDevice(deviceObject);
}

if (g_DriverUnloadRoutine != NULL)
{
        g_DriverUnloadRoutine(DriverObject);
}
} // DriverUnload

//=============================================================================
NTSTATUS
IOCTLDeviceControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
DPF_ENTER("[IOCTLDeviceControl]");

PIO_STACK_LOCATION irpSp;

PAGED_CODE();

irpSp = IoGetCurrentIrpStackLocation(Irp);

switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
case 0:
default:
        return PcDispatchIrp(DeviceObject, Irp);
}
} // IOCTLDeviceControl
******************

WDMAUDIODEV addresses:
Post message: mailto:wdmaudiodev@xxxxxxxxxxxxx
Subscribe:    mailto:wdmaudiodev-request@xxxxxxxxxxxxx?subject=subscribe
Unsubscribe:  mailto:wdmaudiodev-request@xxxxxxxxxxxxx?subject=unsubscribe
Moderator:    mailto:wdmaudiodev-moderators@xxxxxxxxxxxxx

URL to WDMAUDIODEV page:
http://www.wdmaudiodev.com/

Other related posts: