服务/驱动程序(.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 并手动保留引用计数;然后使用一些消息发送给驱动程序客户端代码可以检索计数。有没有办法在不改变驱动程序的情况下做到这一点?
您不需要任何引用计数(系统已经为您完成此操作),也不需要等待客户。如果您想卸载/删除驱动程序 - 只需执行此操作即可。如果驱动程序上仍然存在打开的文件(更确切地说在某些 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;
}