Search code examples
winapiencryptionusbusb-drive

Capture encrypted USB decryption event?


hi I am using a Kingston DT4000 G2 USB drive with password protected.

I could track disk plug in & out event under windows by calling RegisterDeviceNotification(), and receive notification by WM_DEVICECHANGE;

While the problem is the media is not available till I input password. Before decryption I could see the device and system will show "Please insert a disk into USB drive (E:)".

But I can't capture the event when data is decrypted and media is really available to me. Is there a such event could be captured using win32?


Solution

  • You could use following sample with GetLockStatus method of the Win32_EncryptableVolume

    #include <windows.h> 
    #include <dbt.h> 
    #include <string>
    #include <initguid.h>
    #include <IoEvent.h>
    #include <iostream>
    #include <comdef.h>
    #include <Wbemidl.h>
    using namespace std;
    
    #pragma comment(lib, "wbemuuid.lib")
    #pragma warning(disable : 4996)
    
    
    // Function prototype 
    LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
    std::string DrivesFromMask(ULONG unitmask);
    UINT32 GetLockStatus();
    
    int main(int argc, char** argv)
    {
        MSG         msg;        // MSG structure to store messages 
        HWND        hwndMain;   // Main window handle 
        WNDCLASSEX  wcx;        // WINDOW class information  
        HDEVNOTIFY  hDevnotify;
        DWORD       len;
    
        DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
    
        // 53F56307-B6BF-11D0-94F2-00A0C91EFB8B 
        GUID FilterGUID = { 0x53F56307,0x0B6BF,0x11D0,{0x94,0xF2,0x00,0xA0,0xC9,0x1E,0xFB,0x8B} };
    
    
        // Initialize the struct to zero 
        ZeroMemory(&wcx, sizeof(WNDCLASSEX));
    
        wcx.cbSize = sizeof(WNDCLASSEX);        // Window size. Must always be sizeof(WNDCLASSEX) 
        wcx.style = 0;                          // Class styles 
        wcx.lpfnWndProc = (WNDPROC)MainWndProc; // Pointer to the callback procedure 
        wcx.cbClsExtra = 0;                     // Extra byte to allocate following the wndclassex structure 
        wcx.cbWndExtra = 0;                     // Extra byte to allocate following an instance of the structure 
        wcx.hInstance = GetModuleHandle(NULL);              // Instance of the application 
        wcx.hIcon = NULL;                       // Class Icon 
        wcx.hCursor = NULL;                     // Class Cursor 
        wcx.hbrBackground = NULL;               // Background brush 
        wcx.lpszMenuName = NULL;                // Menu resource 
        wcx.lpszClassName = "USB";              // Name of this class 
        wcx.hIconSm = NULL;                     // Small icon for this class 
    
        // Register this window class with MS-Windows 
        if (!RegisterClassEx(&wcx))
            return 0;
    
        // Create the window 
        hwndMain = CreateWindowEx(0,// Extended window style 
            "USB",          // Window class name 
            "",             // Window title 
            WS_POPUP,       // Window style 
            0, 0,           // (x,y) pos of the window 
            0, 0,           // Width and height of the window 
            NULL,           // HWND of the parent window (can be null also) 
            NULL,           // Handle to menu 
            GetModuleHandle(NULL),      // Handle to application instance 
            NULL);          // Pointer to window creation data 
    
    // Check if window creation was successful 
        if (!hwndMain)
            return 0;
    
        // Make the window invisible 
        ShowWindow(hwndMain, SW_HIDE);
    
        // Initialize device class structure  
        len = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
        memset(&NotificationFilter, 0, len);
    
        NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
        NotificationFilter.dbcc_devicetype = 5;         // DBT_DEVTYP_DEVICEINTERFACE; 
        NotificationFilter.dbcc_classguid = FilterGUID;
    
        // Register 
        hDevnotify = RegisterDeviceNotification(hwndMain, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
    
        if (hDevnotify == NULL)
            return 0;
    
        // Process messages coming to this window 
        while (GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    
        // return value to the system 
        return msg.wParam;
    }
    
    HDEVNOTIFY RegisterDevice(HWND hWnd, PDEV_BROADCAST_DEVICEINTERFACE PdevDEVICEINTERFACE)
    {
        DEV_BROADCAST_HANDLE broadcast = { 0 };
        broadcast.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
        broadcast.dbch_devicetype = DBT_DEVTYP_HANDLE;
        broadcast.dbch_handle = CreateFile(PdevDEVICEINTERFACE->dbcc_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
        return RegisterDeviceNotification(hWnd, &broadcast, DEVICE_NOTIFY_WINDOW_HANDLE);
    }
    HDEVNOTIFY hDevNotify = NULL;
    LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        PDEV_BROADCAST_VOLUME PdevVolume;
        PDEV_BROADCAST_DEVICEINTERFACE PdevDEVICEINTERFACE;
        std::string drvs;
        static UINT32 g_LockedDrivesMask;
        switch (msg)
        {
        case WM_DEVICECHANGE:
            switch (wParam)
            {
                // A device or piece of media has been inserted and is now available 
            case DBT_CUSTOMEVENT:
            {
                DEV_BROADCAST_HDR* hdr = (DEV_BROADCAST_HDR*)lParam;
                switch (hdr->dbch_devicetype)
                {
                case DBT_DEVTYP_HANDLE:
                    UINT32 LockedDrivesMask = GetLockStatus();
                    UINT32 result = LockedDrivesMask ^ g_LockedDrivesMask;
                    if (result)
                    {
                        for (int i = 0; i < 26 && result; ++i)
                        {
                            if (result & 0x1)
                            {
                                if (0 == (LockedDrivesMask & (0x1 << i)))
                                    printf("%c: unlock!\n", i + 'A');
                            }
                            result = result >> 1;
                        }
                    }
                    g_LockedDrivesMask = LockedDrivesMask;
                    break;
                }
            }
            break;
            case DBT_DEVICEARRIVAL:
                PdevDEVICEINTERFACE = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;
    
                switch (PdevDEVICEINTERFACE->dbcc_devicetype)
                {
                    // Class of devices 
                case DBT_DEVTYP_DEVICEINTERFACE:
                    g_LockedDrivesMask = GetLockStatus();
                    hDevNotify = RegisterDevice(hwnd, PdevDEVICEINTERFACE);
                    break;
                    // Logical volume 
                case DBT_DEVTYP_VOLUME:
                    PdevVolume = (PDEV_BROADCAST_VOLUME)lParam;
                    drvs = DrivesFromMask(PdevVolume->dbcv_unitmask);
                    for (UINT i = 0; i < drvs.length(); i++)
                        printf("Drive %c:\\ connected\n", drvs[i]);
                }
                break;
            case DBT_DEVICEREMOVEPENDING:
                PdevDEVICEINTERFACE = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;
                UnregisterDeviceNotification(hDevNotify);
            }
            break;
    
        default:
            // Call the default window handler 
            return DefWindowProc(hwnd, msg, wParam, lParam);
        }
    
        return 0;
    }
    
    std::string DrivesFromMask(ULONG unitmask)
    {
        char i;
        std::string drv = "";
        for (i = 0; i < 26 && unitmask; ++i)
        {
            if (unitmask & 0x1)
            {
                drv += i + 'A';
            }
            unitmask = unitmask >> 1;
        }
        return drv;
    }
    
    
    UINT32 GetLockStatus()
    {
        HRESULT hres;
        hres = CoInitializeEx(0, COINIT_MULTITHREADED);
        hres = CoInitializeSecurity(
            NULL,
            -1,
            NULL,
            NULL,
            RPC_C_AUTHN_LEVEL_DEFAULT,
            RPC_C_IMP_LEVEL_IMPERSONATE,
            NULL,
            EOAC_NONE,
            NULL
        );
    
        IWbemLocator* pLoc = NULL;
        hres = CoCreateInstance(
            CLSID_WbemLocator,
            0,
            CLSCTX_INPROC_SERVER,
            IID_IWbemLocator, (LPVOID*)&pLoc);
    
        IWbemServices* pSvc = NULL;
        hres = pLoc->ConnectServer(
            _bstr_t(L"Root\\CIMV2\\Security\\MicrosoftVolumeEncryption"), // Object path of WMI namespace
            NULL,
            NULL,
            0,
            NULL,
            0,
            0,
            &pSvc
        );
    
        hres = CoSetProxyBlanket(
            pSvc,
            RPC_C_AUTHN_WINNT,
            RPC_C_AUTHZ_NONE,
            NULL,
            RPC_C_AUTHN_LEVEL_CALL,
            RPC_C_IMP_LEVEL_IMPERSONATE,
            NULL,
            EOAC_NONE
        );
    
        IEnumWbemClassObject* pEnumerator = NULL;
        wstring strQuery = L"SELECT * FROM Win32_EncryptableVolume";
        hres = pSvc->ExecQuery(BSTR(L"WQL"), BSTR(strQuery.c_str()),
            WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);
    
        IWbemClassObject* pclsObj = NULL;
        IWbemClassObject* pOutParams = NULL;
        ULONG uReturn = 0;
        UINT32 mask = 0;
        while (pEnumerator)
        {
            UINT32 bit = 0;
            hres = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
    
            if (0 == uReturn || FAILED(hres))
                break;
    
            IWbemClassObject* pClass = NULL;
            hres = pSvc->GetObject(BSTR(L"Win32_EncryptableVolume"), 0, NULL, &pClass, NULL);
    
            VARIANT val;
            hres = pclsObj->Get(L"DriveLetter", 0, &val, 0, NULL);
            bit = val.bstrVal[0] - 'A';
            
    
            IWbemClassObject* pInParamsDefinition = NULL;
            hres = pClass->GetMethod(L"GetLockStatus", 0, NULL, NULL);
    
            VARIANT var;
            pclsObj->Get(L"__PATH", 0, &var, NULL, NULL);
    
            hres = pSvc->ExecMethod(var.bstrVal, _bstr_t(L"GetLockStatus"), 0,
                NULL, NULL, &pOutParams, NULL);
            VARIANT varReturnValue;
            hres = pOutParams->Get(_bstr_t(L"LockStatus"), 0,
                &varReturnValue, NULL, 0);
            if (varReturnValue.iVal)
            {
                mask |= 0x1 << bit;
            }
            VariantClear(&val);
            VariantClear(&var);
            VariantClear(&varReturnValue);
    
            pclsObj->Release();
            pClass->Release();
            pOutParams->Release();
            pOutParams = NULL;
        }
        pEnumerator->Release();
        pLoc->Release();
        pSvc->Release();
        CoUninitialize();
        return mask;
    }
    

    But please note that due to the Security Considerations, this sample must be run as admin.

    Or without administrator privileges, you could use the polling method in this example:

    https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/e0585eca-31fa-4fe4-873d-d87934cbbf9d/thread-not-working-if-winmain-arg-is-2?forum=windowssdk