Sysdriver: How to capture Input data and play user audio application data

Hi sir, I tried to implement ioctl interface. But, whenever I added the IOCTL interface, fake mic and fake speaker are not visible in the devices list(Recording & Playback). My IOCTL implementation is: #define MTCS_IOCTL_SUPPORT 1 #if MTCS_IOCTL_SUPPORT Dispatch_type(IRP_MJ_CREATE) DRIVER_DISPATCH CSMTIOCtlCreate; //function Body has implemented Dispatch_type(IRP_MJ_CLOSE) DRIVER_DISPATCH CSMTIOCtlClose; //function Body has implemented Dispatch_type(IRP_MJ_DEVICE_CONTROL) DRIVER_DISPATCH CSMTIOCtlDeviceControl; //function Body has implemented UNICODE_STRING referenceIOCtlString; UNICODE_STRING linkIOCtlString; PDEVICE_OBJECT m_pPhysicalIOCtldeviceObject; #define CSMT_IOCTL_NT_DEVICE_NAME L"\Device\Mtctldevice" // IOCTL GUID DEFINE_GUIDSTRUCT(“05513b4d-6462-45e1-8cbd-0a43ac176b91”, MTCS_IOCTL_AUDIO); #define MTCS_IOCTL_AUDIO DEFINE_GUIDNAMED(MTCS_IOCTL_AUDIO) #define IOCTL_CSMT_READ_METHOD_BUFFERED CTL_CODE(FILE_DEVICE_UNKNOWN, 0x900, METHOD_BUFFERED, FILE_ANY_ACCESS) #endif #pragma code_seg(“PAGE”) NTSTATUS CSMTIOCtlDeviceControl ( In DEVICE_OBJECT* _DeviceObject, Inout IRP* _Irp ) { NTSTATUS ntStatus = STATUS_SUCCESS; PIO_STACK_LOCATION irpStack; ULONG ControlCode; //ULONG inBufLength; // Input buffer length ULONG outBufLength; // Output buffer length PCHAR /*inBuf,*/ outBuf; // pointer to Input and output buffer PCHAR data = “This String is from Device Driver !!!”; size_t datalen = strlen(data) + 1;//Length of data including null PAGED_CODE(); UNREFERENCED_PARAMETER(_DeviceObject); ASSERT(_DeviceObject); ASSERT(_Irp); irpStack = IoGetCurrentIrpStackLocation(_Irp); ControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode; switch (ControlCode) { case IOCTL_CSMT_READ_METHOD_BUFFERED: outBufLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength; if (outBufLength != 0) { outBuf = (PCHAR)_Irp->AssociatedIrp.SystemBuffer; // // Write to the buffer over-writes the input buffer content // RtlCopyBytes(outBuf, data, outBufLength); // // Assign the length of the data copied to IoStatus.Information // of the Irp and complete the Irp. // _Irp->IoStatus.Information = (outBufLength < datalen ? outBufLength : datalen); // // When the Irp is completed the content of the SystemBuffer // is copied to the User output buffer and the SystemBuffer is // is freed. // } else { _Irp->IoStatus.Information = 0; } ntStatus = STATUS_SUCCESS; break; default: ntStatus = STATUS_INVALID_PARAMETER; _Irp->IoStatus.Information = 0; break; } if (ntStatus != STATUS_INVALID_PARAMETER) { ntStatus = STATUS_SUCCESS; } _Irp->IoStatus.Status = ntStatus; IoCompleteRequest(_Irp, IO_NO_INCREMENT); return ntStatus; } IOCTL Interface in Adddevice() function: RtlInitUnicodeString(&referenceIOCtlString, CSMT_IOCTL_NT_DEVICE_NAME); ioctlStatus = IoRegisterDeviceInterface( PhysicalDeviceObject, &MTCS_IOCTL_AUDIO, NULL, &referenceIOCtlString ); if (NT_SUCCESS(ioctlStatus)) { DbgPrint(" Registered Device Status %d Name Length %d \n", ioctlStatus, referenceIOCtlString.Length); ioctlStatus = IoSetDeviceInterfaceState(&referenceIOCtlString, TRUE); if (NT_SUCCESS(ioctlStatus)) { DbgPrint(" Ready for Communication Status %d \n", ioctlStatus); } } Code is building properly and Driver package is also created properly. But, fake mic and fake speaker are not visible. Is there any line creating that problem?, sir. Or, can you suggest me to modify something so that mic & speaker will be in sound list.

Sir, I am bit confusing with name conventions. I want to understand in a practical manner In sysvad: 1. Adapter card - sound card 2. Device - Virtual mic and virtual speaker 3.Adapter driver - code which can communicate with sound card 4.Device object - Virtual mic and virtual speaker’s object Adddevice() function creates the function device objects for Virtual mic and virtual speaker’s object so that IRP can be given to devices Here, Physical device and device are diiferent? Is my understanding correct, sir?

Did you change DriverEntry to install your dispatch routines in the DriverObject after PcInitializeAdapterDriver does its thing?

You need to remember that ALL communication with streaming drivers happens through ioctls. The Audio Engine is sending your driver boatloads of ioctl requests to query your properties, negotiate your format, set the framing, and even stream the data. If you installed your own dispatcher, then you are returning an error for all of the KS ioctls.

If you get an ioctl that you don’t recognize, you need to call PcDispatchIrp so the Port Class driver can do what it would have done if you weren’t there.

Yes, the Port Class driver invents its own terminology, some of which conflicts with normal driver usage and the Kernel Streaming model upon which it is based.

An “adapter” is a sound card. A collection of endpoints. An “adapter” is a device in the WDM and KS sense. Your fake sound card will have one adapter, which means you will have one DEVICE_OBJECT.

A “subdevice” in Port Class is a high-level concept. You’ll get one subdevice for topology, and one subdevice for WaveCyclic or WaveRT, each one represented by a COM object… The WaveCyclic or WaveRT object then has separate streams for capture (microphone) and render (speaker), again represented by COM objects.

The “subdevice” is a filter in the KS sense (like DirectShow). Each filter has multiple pins, which the Port Class driver maps to streams.

Thank you sir. I added PcDispatchIrp into my dispatcher and invoked whenever it receives other than my IRP. Now I could see the speaker/mic in the sound card control panel list.

Now, I am trying to connect my device from my application (to exchange some data) using CM_Get_Device_Interface_List and CreateFileW APIs. But is returned CreateFileW failed with error No 2.

The can able to see my device in the registry list - Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\DeviceClasses{05513b4d-6462-45e1-8cbd-0a43ac176b91}##?#ROOT#MEDIA#0000#{05513b4d-6462-45e1-8cbd-0a43ac176b91}

From CM_Get_Device_Interface_List , received DeviceName as (\?\ROOT#MEDIA#0000#{05513b4d-6462-45e1-8cbd-0a43ac176b91}) then I called
status = GetDevicePath((LPGUID)&GUID_MTCS_DEVINTERFACE, completeDeviceName,
sizeof(completeDeviceName) / sizeof(completeDeviceName[0]));
if (status == FALSE)
{
return INVALID_HANDLE_VALUE;
}

Below is my code change in SYSVAD:

In DriverEntry → Assigned my own dispatcher for IRP_MJ_DEVICE_CONTROL

     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MTCS_IOCtlDeviceControl;

in ADD device – Registered my device with 05513b4d-6462-45e1-8cbd-0a43ac176b91

ioctlStatus = IoRegisterDeviceInterface(
   PhysicalDeviceObject,
  &MTCS_IOCTL_AUDIO,
   NULL,
   &referenceIOCtlString
);

if (NT_SUCCESS(ioctlStatus)) 
{
   ioctlStatus = IoSetDeviceInterfaceState(&referenceIOCtlString, TRUE);
}

Dispatcher Handler:

irpStack = IoGetCurrentIrpStackLocation(_Irp);

if(irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL)
{
ControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
if(ControlCode == IOCTL_CSMT_READ_METHOD_BUFFERED) {
_Irp->IoStatus.Information = 0;
ntStatus = STATUS_SUCCESS
_Irp->IoStatus.Status = ntStatus;
IoCompleteRequest(_Irp, IO_NO_INCREMENT);
return ntStatus; // Returned if receives my IRP
}
}
ntStatus = PcDispatchIrp(_DeviceObject, _Irp);

You mentioned that you’re calling CreateFileW. You shouldn’t explicitly add the “A” or “W” suffix. That’s just a bug waiting to happen. If you are building the app as Unicode, then the compiler will automatically call CM_Get_Device_Interface_ListW and CreateFileW. If you find yourself having to cast between LPSTR and LPWSTR, then you have made a mistake.

Remember that you have to call RtlFreeUnicodeString on referenceIOCtlString after you’re through with it.

The Port Class driver seems to reject file names that it doesn’t understand. I’ve always had to specify a reference string suffix of L"Wave" when calling IoRegisterDeviceInterface.

Sir, I have corrected my code as per your suggestion. But still CreateFile is failed with error no 2. Looks like CreateFile failure for device control object is due to IRP_MJ_CREATE & IRP_MJ_CLOSE dispatcher. When I add and overwrite with my own dispatcher for these two major functions then CreateFile is success for my device control object. But all the other KS ioctls are failed. Please suggest how to differentiate the device object in my CREATE dispatcher to identify the request is from my control device object. So that I can the complete the IRP and return an appropriate NTSTATUS . Otherwise I will continue PcDispatchIrp for handling KS ioctls

You don’t have a control device object, assuming you did as described above. You just have a symbolic link into the primary device object. You shouldn’t need to interfere with the CreateFile process. If you want to, I suppose you could use a custom reference suffix when you register your device interface, then check for that in your CreateFile handler. If you see it, return success. If not, pass it to PcDispatchIrp. However, I’ve never had to do that.

Yes sir. I created symbolic link into the primary device object . In my application I am trying to access that device to send/receive data using my IOCTL. In my application, i am using CM_Get_Device_Interface_List() to get the device name from GUID, then I am calling CreateFile with OPEN_EXISTING to open my device . But CreateFile (hDev ==-1) Is returned with error 2. Not sure what is the problem. I tried debugging but could not. Please help me sir. cr = CM_Get_Device_Interface_List( InterfaceGuid, NULL, deviceInterfaceList, deviceInterfaceListLength, CM_GET_DEVICE_INTERFACE_LIST_PRESENT); if (cr != CR_SUCCESS) { } hr = StringCchCopy(DevicePath, BufLen, deviceInterfaceList); if (FAILED(hr)) { } hDev = CreateFile( DevicePath, GENERIC_READ , 0, NULL, OPEN_EXISTING, 0, NULL );

Did you print out the file name, to see if it looks like a hardware ID string? Showing snippets like this isn’t enough. It is so easy to make character set mistakes, and we can’t see that unless we can see the declarations of deviceInterfaceList and DevicePath. Where did you get BufLen? Where did you get deviceInterfaceListLength?

You probably want GENERIC_READ|GENERIC_WRITE, and you may need to allow sharing, with FILE_SHARE_READ|FILE_SHARE_WRITE, but that won’t cause ERROR_FILE_NOT_FOUND.

Thank you sir, I could able to communicate with driver through IOCTL. I could able to get notifications of fake speaker & Mic also, if any application uses them. My process application is ready with Real mic data I am stuck at: 1.From which buffer audio engine reads the data and provide it to skype (By searching forums, we have to copy our data to generate sine buffer. Isnt it relate to DMA?, If not , can I directly copy my data to that buffer? 2. Can I create a separate cyclic buffer to copy my real data? If so, Can you suggest me, where I have to create my cyclic Buffer so that audio engine can read from it? In which function of sysvad, we can get calls from audio engine for data filling? can you please give some explanation about them sir!!!

1.: You couldn’t figure this out from the code? In CMiniportWaveRTStream::WriteBytes, it calls GenerateSine to put new microphone data into m_pDmaBuffer. If there were real hardware involved, then yes, there would be DMA going on. In this case, you are pretending to be the hardware.

2.: That IS the cyclic buffer. That’s it. You can trace its usage in the module. Because its timing is so tight, you may want your own buffer to communicate with your app, although you might be able to do both ends with one buffer.

Thank you for your help sir, I could able to send my audio to sysvad and can take from sysvad too. I have created a circular buffer with below function. Somewhere in the source said that this function will create non contiguous memory. CS_Frame = (BYTE*)ExAllocatePoolWithTag( NonPagedPoolNx, m_FrameSize, SYSVAD_POOLTAG1); But, it is working properly as of now. Is there any problem with this call for creating memory,sir?

If there is no hardware involved, then non-contiguous memory is not a concern. I tend to use “operator new”, but that’s just a wrapper around ExAllocatePoolWithTag.

By the way, if “CS_Frame” is a class member variable, then you should think about using the m_ prefix. That’s the only remnant of Hungarian notation that I still cling to, just because otherwise it’s not clear where the variable is defined.

Yes sir, Now Sysvad’s fake mic and speaker is able to communicate with the processing application. I want to get Digital signature for driver in order to avoid test-sign, sir.

As you suggested earlier, there are two ways to get certification from Microsoft.

  1. WHQL
    2.Attestation signing

Which service is preferable, sir?. I am completely new to this,sir. And I am learning about this.

So far :
I am understanding about HLK installations and tests.
If the driver has passed all the test cases which are provided by HLK, will it get digital signature,sir?
Else, I have to submit the package after test to dashboard.

 Can you guide me the way to get certification!!!!! Or Please paste any reference link about certification

Thank you

Google is your friend, sir: https://www.osr.com/blog/2019/06/03/three-plus-years-later-driver-signing-still-baffles/

Peter

Yes Peter, thanks for the link.

              Thank you very much for you support till now, @Tim_Roberts. 
              Sysvad is working fine with less delay ~60ms delay. But, while installing in some PCs(Windows 10), blue screen error is coming  and not able to load the actual PC. I can able resolve but it should not happen.

                1.Can you please suggest me the test cases to pass for sysvad to confirm whether sysvad is working properly or not. So that I can able to test that on multiple PCs. If any document or link available. Please paste here.

               2.I could able to remove unnecessary mic and speaker pairs. But I am not able to figure out, how to remove APO?

                 Please post your suggestions for the above!!

Thanks,
Dinesh

… blue screen error is coming … I can able resolve but it should not happen.

No, it shouldn’t happen. Are you saying you fixed this, or are you asking for assistance?

Can you please suggest me the test cases to pass?

Do you have a test program that routes data through your sysvad and forwards it to/from the system microphone and speakers? If you’re getting sound, that’s means a lot of stuff is working. Does your device look like a real device in the sound control panel? There are WHQL tests for audio devices, if you want to go that far. They have some very picky requirements for latency and response time.

I could able to remove unnecessary mic and speaker pairs. But I am not able to figure out, how to remove APO?

Why do you have to remove anything? Where did these come from? Are you saying you don’t know how to eliminate the APO from the sysvad installation package? APOs are all about the registry. If you remove their registration parts from the INF, then they won’t be installed.

Thanks sir, No, it shouldn’t happen. Are you saying you fixed this, or are you asking for assistance? Doing some efforts after blue screen, I could able to load my os. But, my question was how to avoid blue screen. As of now, it is working fine. As per attestation signing: I have read documents about it and got to know the steps. I want to know about the validity of signature of driver. Once the driver got signature, can I use the driver for a life time or renew the digital sign of driver after some period of time. If I am not wrong, driver signature time validity does not depend on EV certificate validity. And, we have to renew the ev certificate after it has expired. Can you tell me about it sir!!! I dont have any prior experience.