Search code examples
c++cwinapidriver

Number of outstanding clients of a service/driver IO file on Windows


After a service/driver (.sys) is started, normally communication happens through a file (client pseudo code):

HANDLE gHandle = CreateFileA("\\\\.\\DriverFile",
    GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL, NULL);
... 
DeviceIoControl(gHandle, MY_MSG_ID, NULL, 0, &refCount, 
   sizeof(refCount), &length, NULL);
...
CloseHandle(gHandle);

Is there a way to know how many clients have open connection to the driver file? One way to impl it, is to handle IRP_MJ_CREATE/IRP_MJ_CLOSE on the driver side and keep ref count manually; then using some message to the driver client code could retrieve the count. Is there a way to do that without involving changes to the driver side?


Solution

  • you not need any reference counting (the system already do this for you) and not need wait for clients. if you want unload/delete driver - simply do this. if still exist opened files on driver (more exactly on some it device) system unload driver, when last file will be closed.

    let do demo with Null.sys driver. try stop/unload it, until exist opened files on it device ( \Device\Null ).

    HRESULT StopDriver(_In_ PCWSTR lpServiceName, _In_ PCWSTR lpDriverName)
    {
        HRESULT hr;
    
        if (SC_HANDLE hSCManager = HR(hr, OpenSCManagerW(0, 0, 0)))
        {
            SC_HANDLE hService = HR(hr, OpenServiceW(hSCManager, lpServiceName, SERVICE_STOP));
    
            CloseServiceHandle(hSCManager);
    
            if (hService)
            {
                SERVICE_STATUS ss;
    
                HR(hr, ControlService(hService, SERVICE_CONTROL_STOP, &ss));
    
                CloseServiceHandle(hService);
    
                if (NOERROR == hr && SERVICE_STOP_PENDING == ss.dwCurrentState)
                {
                    while (STATUS_OBJECT_TYPE_MISMATCH == 
                        (hr = DoesDriverObjectExist(lpDriverName)))
                    {
                        Sleep(100);
                    }
                }
            }
        }
    
        switch (hr)
        {
        case ERROR_SERVICE_NOT_ACTIVE:
        case STATUS_OBJECT_NAME_NOT_FOUND:
            hr = S_OK;
            break;
        default:
            DbgPrint("error = %x\n", hr);
            break;
        }
    
        return hr;
    }
    

    for check are driver in memory we can use next function

    NTSTATUS DoesDriverObjectExist(PCWSTR lpDriverName)
    {
        UNICODE_STRING ObjectName;
        RtlInitUnicodeString(&ObjectName, lpDriverName);
        OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName, OBJ_CASE_INSENSITIVE };
        IO_STATUS_BLOCK iosb;
        NTSTATUS status = NtOpenFile(&oa.RootDirectory, SYNCHRONIZE, &oa, &iosb, 0, 0);
    
        if (0 <= status)
        {
            __debugbreak();// this never must be !
            NtClose(oa.RootDirectory);
        }
        return status;
    }
    

    in case Null.sys the \Driver\Null object will be, until driver in memory. we can try open this object with NtOpenFile. we fail in any case (impossible open driver object from user mode and with this api). but error code will be different.

    STATUS_OBJECT_TYPE_MISMATCH - mean that \Driver\Null exist and STATUS_OBJECT_NAME_NOT_FOUND - mean that \Driver\Null not exist

    for simulate client during driver stop we can use next code

    ULONG WINAPI ClientThread(HANDLE hEvent)
    {
        HANDLE hFile = CreateFileW(L"\\\\?\\NUL", 0, 0, 0, OPEN_EXISTING, 0, 0);
        
        SetEvent(hEvent);
    
        if (INVALID_HANDLE_VALUE != hFile)
        {
            MessageBoxW(0, 0, L"\\\\?\\NUL", MB_ICONINFORMATION);
            CloseHandle(hFile);
        }
    
        return 0;
    }
    
    void mmm()
    {
        if (HANDLE hEvent = CreateEvent(0, TRUE, FALSE, 0))
        {
            if (HANDLE hThread = CreateThread(0, 0, ClientThread, hEvent, 0, 0))
            {
                CloseHandle(hThread);
    
                WaitForSingleObject(hEvent, INFINITE);
            }
    
            CloseHandle(hEvent);
        }
    
        StopDriver(L"Null", L"\\Driver\\Null");
    }
    

    so we first start client and wait until it open device on Null.sys ( L"\\\\?\\NUL" ) and then try stop driver - StopDriver(L"Null", L"\\Driver\\Null"); because we wait here for demo - we will be spin in loop, until messagebox in ClientThread will be not closed. in real code - you usual not need call DoesDriverObjectExist and wait for other value than STATUS_OBJECT_TYPE_MISMATCH - you can simply delete service or dont care at all. all this code - for demo and better understand all.

    and util function

    template <typename T> 
    T HR(HRESULT& hr, T t)
    {
        hr = t ? NOERROR : GetLastError();
        return t;
    }