Obtaining a handle to a USB WebCam (Windows 10/CPP)

I’m trying to obtain a handle to my USB WebCam (Microsoft LifeCam HD-3000 ) device. I dont want to use it in a conventional way so usage of any multimedia framework are not considerable. I need to be able (if possible) to send to it IRP’s via DeviceIoControl etc. Reversing its driver written in KMDF I found a call to WdfDeviceCreateDeviceInterface and GUID passed as an argument:

Pseudo-code
__int64 __fastcall EvtDeviceAdd(__int64 wdfDriver, __int64 DeviceInit)
{
    WdfFdoInitSetFilter(WdfDriverGlobals, DeviceInit);
    WdfDeviceInitSetDeviceType(WdfDriverGlobals, DeviceInit, 0x23i64);// FILE_DEVICE_VIDEO
    WdfDeviceInitSetIoType(WdfDriverGlobals, DeviceInit, 3i64);// WdfDeviceIoDirect
    memset(&fdoAttributes, 0, 0x38ui64);
    fdoAttributes.ContextTypeInfo = (PVOID)off_FFFFF80765075020;
    fdoAttributes.Size = 56;
    fdoAttributes.ExecutionLevel = 1;
    fdoAttributes.SynchronizationScope = 1;
    WdfDeviceInitSetIoInCallerContextCallback(WdfDriverGlobals, DeviceInit, EvtIoInCallerContext);
    v1 = WdfDeviceCreate(WdfDriverGlobals, &DeviceInit, &fdoAttributes, &device);// WdfDeviceCreate
    v1 = WdfDeviceCreateDeviceInterface(WdfDriverGlobals, device, &stru_FFFFF807650740C8, 0i64);
  return (unsigned int)v1;
}

.rdata:FFFFF807650740C8 stru_FFFFF807650740C8 dd 0B94D388Ah           ; Data1
.rdata:FFFFF807650740C8                                         ; DATA XREF: internalEvtDeviceAdd+351↓o
.rdata:FFFFF807650740C8                 dw 7331h                ; Data2
.rdata:FFFFF807650740C8                 dw 4E5Eh                ; Data3
.rdata:FFFFF807650740C8                 db 8Bh, 0Fh, 8, 16h, 0Eh, 0A1h, 0F7h, 6; Data4

Python>getGUID(0xFFFFF807650740C8)
b94d388a-7331-4e5e-8b0f-08160ea1f706

Having an interface GUID I used the WinObjEx64 to find a symlink to created device:

With symlink I attempted to open a handle:

int main()
{
    LPWSTR errormsg = NULL;
    DWORD errorCode;
    HANDLE h = CreateFile(L"\\\\.\\USB#VID_045E&PID_0779&MI_00#6&2bd7a9d&0&0000#{b94d388a-7331-4e5e-8b0f-08160ea1f706}",
                GENERIC_READ, // Only read access
                0,            // FILE_SHARE_READ | FILE_SHARE_WRITE
                NULL,         // no SECURITY_ATTRIBUTES structure
                OPEN_EXISTING,// No special create flags
                0,            // No special attributes
                NULL);        // No template file

    errorCode = GetLastError();
    printf("Error code: 0x%x\n", errorCode);
    if (h == INVALID_HANDLE_VALUE)
    {
        FormatMessageW(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM |
            FORMAT_MESSAGE_IGNORE_INSERTS,
            0,
            errorCode,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            (LPWSTR)&errormsg,
            0, NULL);

        wprintf(L"Error : %s\n", errormsg);
    }

    return 0;
}

Output:

Error code: 0x2
Error : The system cannot find the file specified.

With such result I decided to obtain a dev path using SetupDi* APIs

void GetInterfaceDevicePath(GUID* guid) {
    DWORD requiredSize;
    int MemberIdx = 0;
    HDEVINFO hDeviceInfoset = SetupDiGetClassDevs(guid, NULL, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
    if (hDeviceInfoset != INVALID_HANDLE_VALUE) {
        SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = { 0 };
        DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
        while (SetupDiEnumDeviceInterfaces(hDeviceInfoset, NULL, guid, MemberIdx, &DeviceInterfaceData)) {
            MemberIdx++;
            SP_DEVINFO_DATA DeviceInfoData = { 0 };
            DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
            SetupDiGetDeviceInterfaceDetail(hDeviceInfoset, &DeviceInterfaceData, NULL, 0, &requiredSize, NULL);
            SP_DEVICE_INTERFACE_DETAIL_DATA* DevIntfDetailData = (SP_DEVICE_INTERFACE_DETAIL_DATA*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
                requiredSize);
            DevIntfDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
            if (SetupDiGetDeviceInterfaceDetail(hDeviceInfoset, &DeviceInterfaceData,
                DevIntfDetailData, requiredSize, &requiredSize, &DeviceInfoData)) {
                printf("DevicePath: %S\n", (TCHAR*)DevIntfDetailData->DevicePath);

                HANDLE h = CreateFileW(DevIntfDetailData->DevicePath,
                    0,
                    0,
                    NULL, 
                    OPEN_EXISTING,
                    0,
                    NULL);
                LPWSTR errormsg = NULL;
                DWORD error = GetLastError();
                printf("Error code: 0x%x\n", error);
                if (h == INVALID_HANDLE_VALUE)
                {
                    FormatMessageW(
                        FORMAT_MESSAGE_ALLOCATE_BUFFER |
                        FORMAT_MESSAGE_FROM_SYSTEM |
                        FORMAT_MESSAGE_IGNORE_INSERTS,
                        0,
                        error,
                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                        (LPWSTR)&errormsg,
                        0, NULL);

                    wprintf(L"Error : %s\n", errormsg);
                }
            }
            HeapFree(GetProcessHeap(), 0, DevIntfDetailData);
        }
        SetupDiDestroyDeviceInfoList(hDeviceInfoset);
    }
}

int main()
{
    GUID deviceGUID;
    CLSIDFromString(L"{b94d388a-7331-4e5e-8b0f-08160ea1f706}", (LPCLSID)&deviceGUID);
    GetInterfaceDevicePath(&deviceGUID);
    return 0;
}

Unfortunately that also failed:

DevicePath: \\?\usb#vid_045e&pid_0779&mi_00#6&2bd7a9d&0&0000#{b94d388a-7331-4e5e-8b0f-08160ea1f706}
Error code: 0x2
Error : The system cannot find the file specified.

Output from livekd about how this driver is attached to the driver/device stack:

||1:lkd> !drvobj \Driver\LifeCamTrueColor
Driver object (ffffc60eba506750) is for:
 \Driver\LifeCamTrueColor

Driver Extension List: (id , addr)
(fffff80684f87330 ffffc60eb19f75f0)  
Device Object list:
ffffc60eb82f54e0  
||1:lkd> !devobj ffffc60eb82f54e0
Device object (ffffc60eb82f54e0) is for:
  \Driver\LifeCamTrueColor DriverObject ffffc60eba506750
Current Irp 00000000 RefCount 0 Type 0000002f Flags 00002000
SecurityDescriptor ffffdb888d07e660 DevExt ffffc60ec0deffb0 DevObjExt ffffc60eb82f5658 
ExtensionFlags (0000000000)  
Characteristics (0x00000100)  FILE_DEVICE_SECURE_OPEN
AttachedDevice (Upper) ffffc60eaba48b40 \Driver\ksthunk
AttachedTo (Lower) ffffc60ec2cdadf0 \Driver\usbvideo
Device queue is not busy.
||1:lkd> !drvobj \Driver\usbvideo
Driver object (ffffc60ebfde7e30) is for:
 \Driver\usbvideo

Driver Extension List: (id , addr)
(fffff8069aa2d130 ffffc60eb19f7560)  
Device Object list:
ffffc60ec2cdadf0  
||1:lkd> !devobj ffffc60ec2cdadf0
Device object (ffffc60ec2cdadf0) is for:
  \Driver\usbvideo DriverObject ffffc60ebfde7e30
Current Irp 00000000 RefCount 0 Type 0000002f Flags 00002000
SecurityDescriptor ffffdb888d07e660 DevExt ffffc60ec2cdaf60 DevObjExt ffffc60ec2cdaf68 
ExtensionFlags (0000000000)  
Characteristics (0x00000100)  FILE_DEVICE_SECURE_OPEN
AttachedDevice (Upper) ffffc60eb82f54e0 \Driver\LifeCamTrueColor
AttachedTo (Lower) ffffc60eb60e60a0 \Driver\usbccgp
Device queue is not busy.
||1:lkd> !drvobj \Driver\ksthunk
Driver object (ffffc60ea7777df0) is for:
 \Driver\ksthunk

Driver Extension List: (id , addr)
(fffff806993f80a0 ffffc60ea772e580)  
Device Object list:
ffffc60eaf67c7b0  ffffc60eaba48b40  ffffc60ea7bc4e10  ffffc60ea79a8e00
ffffc60ea7a15de0  ffffc60ea771bdf0  

||1:lkd> !devobj ffffc60eaba48b40
Device object (ffffc60eaba48b40) is for:
 000000d4 \Driver\ksthunk DriverObject ffffc60ea7777df0
Current Irp 00000000 RefCount 0 Type 0000002f Flags 00002040
SecurityDescriptor ffffdb888d07e660 DevExt ffffc60eaba48c90 DevObjExt ffffc60eaba48c98 
ExtensionFlags (0000000000)  
Characteristics (0x00000100)  FILE_DEVICE_SECURE_OPEN
AttachedTo (Lower) ffffc60eb82f54e0 \Driver\LifeCamTrueColor
Device queue is not busy.

Also a screens from DeviceTree :


Thanks!

I dont want to use it in a conventional way so usage of any multimedia framework are not considerable.
I need to be able (if possible) to send to it IRP’s via DeviceIoControl etc.

What you’re doing here is silly. You’re reverse-engineering this as if it was some sort of state secret. It’s not. Your USB camera is an AVStream device. It communicates using the Kernel Streaming APIs, like IOCTL_KS_PROPERTY and IOCTL_KS_READ_STREAM. However, those ioctls are poorly defined, and there are many hidden rules and secret handshakes. KS devices are intended to be talked to through ksproxy.ax (for DirectShow) and devproxy.dll (for Media Foundation).

There is absolutely nothing you can do with that camera that cannot be done much more simply and easily through the defined interfaces. You can’t reprogram the camera, and you can’t force it to do things that aren’t exposed in its USB descriptors. If you don’t like DirectShow or Media Foundation, you can use OpenCV. Well understood, well supported, and open source.

What are you actually hoping to do that you think needs special access?

Hi Tim, thank you for the answer.
Well, I dont count on that I will find a “super special hidden functionality” of that cam but I’m trying to audit its driver in a context of security.
After initial recon I noticed that it registers handlers for EvtIoDeviceControl and EvtIoInCallerContext which are fragments of code where the attacker can provide more less controllable by him data and in that way trigger some vulnerabilities on windows kernel level.
So its definitely not a silly thing ;).

The driver that does all the work is usbvideo.sys, a 20-year-old in-the-box driver with millions of hours of real-life use.

I don’t know what the LifeCamTrueColor.sys filter driver brings to the party, but living where it is, it won’t see anything other than Kernel Streaming ioctls, and those ioctls will already have been processed and validated by the Ksthunk driver that sits on top of it. There’s very little opportunity for you to slip something untoward in there. I suppose you could try fuzzing some KS ioctls, but ksthunk does pretty thorough validation. It has to, because the KS ioctls are all METHOD_NEITHER that don’t get any help from the I/O system.