Windows 上服务/驱动程序 IO 文件的未完成客户端数量

问题描述 投票:0回答:1

服务/驱动程序(.sys)启动后,通常通过文件(客户端伪代码)进行通信:

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);

有没有办法知道有多少客户端打开了与驱动程序文件的连接? 实现它的一种方法是在驱动程序端处理 IRP_MJ_CREATE/IRP_MJ_CLOSE 并手动保留引用计数;然后使用一些消息发送给驱动程序客户端代码可以检索计数。有没有办法在不改变驱动程序的情况下做到这一点?

c++ c winapi driver
1个回答
0
投票

您不需要任何引用计数(系统已经为您完成此操作),也不需要等待客户。如果您想卸载/删除驱动程序 - 只需执行此操作即可。如果驱动程序上仍然存在打开的文件(更确切地说在某些 IT 设备上)系统卸载驱动程序,最后一个文件将被关闭。

让我们使用 Null.sys 驱动程序进行演示。尝试停止/卸载它,直到设备上存在打开的文件(

\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;
}

为了检查内存中的驱动程序,我们可以使用下一个函数

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;
}

如果出现 Null.sys,则

\Driver\Null
对象将一直存在,直到驱动程序进入内存。我们可以尝试用
NtOpenFile
打开这个对象。无论如何我们都会失败(不可能从用户模式并使用此 api 打开驱动程序对象)。但错误代码会有所不同。

STATUS_OBJECT_TYPE_MISMATCH
- 表示
\Driver\Null
存在,
STATUS_OBJECT_NAME_NOT_FOUND
- 表示
\Driver\Null
不存在

为了在驾驶员停止期间模拟客户端,我们可以使用下一个代码

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");
}

所以我们首先启动客户端并等待它在 Null.sys (

L"\\\\?\\NUL"
) 上打开设备,然后尝试停止驱动程序 -
StopDriver(L"Null", L"\\Driver\\Null");
因为我们在这里等待演示 - 我们将循环旋转,直到
 中的消息框ClientThread
不会关闭。在真实代码中 - 您通常不需要调用
DoesDriverObjectExist
并等待
STATUS_OBJECT_TYPE_MISMATCH
之外的其他值 - 您可以简单地删除服务或根本不关心。所有这些代码 - 用于演示并更好地理解所有内容。

和util函数

template <typename T> 
T HR(HRESULT& hr, T t)
{
    hr = t ? NOERROR : GetLastError();
    return t;
}
© www.soinside.com 2019 - 2024. All rights reserved.