c++debuggingexceptionbuffer-overflowwpd

WPD API Detect if Device is a Phone?


EDIT: Full source code was requested. Below is a barebones implementation in order to replicate the bug. Content enumeration is removed, however the crash ocurrs on the first object call anyway. In this case, the WPD_DEVICE_OBJECT_ID object.

LINK TO CPP (Bug begins at line 103)

LINK TO QMAKE.PRO (I'm using Qt)


In my project I use the WPD API to read the contents of a mobile device. I followed the API to a tee and have successfully implemented content enumeration.

However, if a USB drive is connected, the WPD API will also sometimes detect that as a device. My program will go ahead and begin content enumeration anyway. I don't want that. I only want to enumerate mobile devices.

The problem is that during content enumeration, when my program attempts to retrieve a property of an object on the USB drive, it crashes. Here are the crash details:

Problem Event Name: BEX
Application Name:   UniversalMC.exe
Application Version:    0.0.0.0
Application Timestamp:  5906a8a3
Fault Module Name:  MSVCR100.dll
Fault Module Version:   10.0.40219.325
Fault Module Timestamp: 4df2be1e
Exception Offset:   0008af3e
Exception Code: c0000417
Exception Data: 00000000
OS Version: 6.1.7601.2.1.0.768.3
Locale ID:  1033
Additional Information 1:   185e
Additional Information 2:   185ef2beb7eb77a8e39d1dada57d0d11
Additional Information 3:   a852
Additional Information 4:   a85222a7fc0721be22726bd2ca6bc946

The crash occurs on this call:

hr = pObjectProperties->GetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &objectName);

hr returns FAILED and then my program crashes.

After some research I've found that exception code c0000417 means a buffer overflow occurred? Correct me if I'm wrong but, is this a vulnerability in the WPD API? If so, how could I detect ahead of time that this device is not a mobile device?

Thanks for your time!


Solution

  • I ended up paying someone to help me pinpoint the issue.

    The problem was that the root object (WPD_DEVICE_OBJECT_ID) would not return an object name no matter what (Not true for all devices).

    The solution was to simply begin content enumeration FROM the root object and only check the names of its children. In my original implementation, I assumed every object has a name, but apparently that is not the case. The root object is the exception.

    Here is a snippet:

    CComPtr<IEnumPortableDeviceObjectIDs> pEnumObjectIDs;
    
    // Print the object identifier being used as the parent during enumeration.
    //qDebug("%ws\n",pszObjectID);
    
    // Get an IEnumPortableDeviceObjectIDs interface by calling EnumObjects with the
    // specified parent object identifier.
    hr = pContent->EnumObjects(0,               // Flags are unused
                                       WPD_DEVICE_OBJECT_ID,     // Starting from the passed in object
                                       NULL,            // Filter is unused
                                       &pEnumObjectIDs);
    
    // Enumerate content starting from the "DEVICE" object.
    if (SUCCEEDED(hr))
    {
        // Loop calling Next() while S_OK is being returned.
        while(hr == S_OK)
        {
            DWORD  cFetched = 0;
            PWSTR  szObjectIDArray[NUM_OBJECTS_TO_REQUEST] = {0};
            hr = pEnumObjectIDs->Next(NUM_OBJECTS_TO_REQUEST,   // Number of objects to request on each NEXT call
                                      szObjectIDArray,          // Array of PWSTR array which will be populated on each NEXT call
                                      &cFetched);               // Number of objects written to the PWSTR array
            if (SUCCEEDED(hr))
            {
                // Traverse the results of the Next() operation and recursively enumerate
                // Remember to free all returned object identifiers using CoTaskMemFree()
                for (DWORD dwIndex = 0; dwIndex < cFetched; dwIndex++)
                {
                    //RECURSIVE CONTENT ENUMERATION CONTINUES HERE
                    //OBJECT NAME CHECKING CONTINUES IN THE RECURSIVE FUNCTION
    
                    // Free allocated PWSTRs after the recursive enumeration call has completed.
                    CoTaskMemFree(szObjectIDArray[dwIndex]);
                    szObjectIDArray[dwIndex] = NULL;
                }
            }
        }
    
    }
    

    The solution is exactly what the sample project shows to do, however, I made the mistake of checking the name of the root object. So don't do that.