You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
345 lines
11 KiB
345 lines
11 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
KernelModeDriverInstall.cpp
|
|
|
|
Abstract:
|
|
|
|
This AppVerifier shim detects if an application
|
|
is attempting to install a kernel mode driver.
|
|
It monitors calls to CreateService and monitors
|
|
the registry where information about drivers is
|
|
stored.
|
|
|
|
Notes:
|
|
|
|
This is a general purpose shim.
|
|
|
|
History:
|
|
|
|
09/30/2001 rparsons Created
|
|
10/03/2001 rparsons Fixed Raid bug # 476193
|
|
11/29/2001 rparsons Fixed Raid bug # 499824
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
IMPLEMENT_SHIM_BEGIN(KernelModeDriverInstall)
|
|
#include "ShimHookMacro.h"
|
|
|
|
BEGIN_DEFINE_VERIFIER_LOG(KernelModeDriverInstall)
|
|
VERIFIER_LOG_ENTRY(VLOG_KMODEDRIVER_INST)
|
|
END_DEFINE_VERIFIER_LOG(KernelModeDriverInstall)
|
|
|
|
INIT_VERIFIER_LOG(KernelModeDriverInstall);
|
|
|
|
APIHOOK_ENUM_BEGIN
|
|
|
|
APIHOOK_ENUM_ENTRY(CreateServiceA)
|
|
APIHOOK_ENUM_ENTRY(CreateServiceW)
|
|
APIHOOK_ENUM_ENTRY(NtSetValueKey)
|
|
|
|
APIHOOK_ENUM_END
|
|
|
|
//
|
|
// Initial size to use for a stack based buffer when
|
|
// performing a Nt registry API call.
|
|
//
|
|
#define MAX_INFO_LENGTH 512
|
|
|
|
//
|
|
// Constant for the 'ControlSet' & 'CurrentrControlSet' key path in the registry.
|
|
//
|
|
#define KMDI_CONTROLSET_KEY L"REGISTRY\\MACHINE\\SYSTEM\\ControlSet"
|
|
#define KMDI_CURCONTROLSET_KEY L"REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet"
|
|
|
|
//
|
|
// Constant for the 'Services' key path.
|
|
//
|
|
#define KMDI_SERVICES_KEY L"Services\\"
|
|
|
|
//
|
|
// Constant for the ValueName that we need to look for.
|
|
//
|
|
#define KMDI_VALUE_NAME L"Type"
|
|
|
|
//
|
|
// Constant for the Value that we need to look for.
|
|
//
|
|
#define KMDI_TYPE_VALUE 0x00000001L
|
|
|
|
//
|
|
// Macros for memory allocation/deallocation.
|
|
//
|
|
#define MemAlloc(s) RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, (s));
|
|
#define MemFree(b) RtlFreeHeap(RtlProcessHeap(), 0, (b));
|
|
|
|
SC_HANDLE
|
|
APIHOOK(CreateServiceA)(
|
|
SC_HANDLE hSCManager, // handle to SCM database
|
|
LPCSTR lpServiceName, // name of service to start
|
|
LPCSTR lpDisplayName, // display name
|
|
DWORD dwDesiredAccess, // type of access to service
|
|
DWORD dwServiceType, // type of service
|
|
DWORD dwStartType, // when to start service
|
|
DWORD dwErrorControl, // severity of service failure
|
|
LPCSTR lpBinaryPathName, // name of binary file
|
|
LPCSTR lpLoadOrderGroup, // name of load ordering group
|
|
LPDWORD lpdwTagId, // tag identifier
|
|
LPCSTR lpDependencies, // array of dependency names
|
|
LPCSTR lpServiceStartName, // account name
|
|
LPCSTR lpPassword // account password
|
|
)
|
|
{
|
|
SC_HANDLE scHandle;
|
|
|
|
scHandle = ORIGINAL_API(CreateServiceA)(hSCManager,
|
|
lpServiceName,
|
|
lpDisplayName,
|
|
dwDesiredAccess,
|
|
dwServiceType,
|
|
dwStartType,
|
|
dwErrorControl,
|
|
lpBinaryPathName,
|
|
lpLoadOrderGroup,
|
|
lpdwTagId,
|
|
lpDependencies,
|
|
lpServiceStartName,
|
|
lpPassword);
|
|
|
|
if (scHandle) {
|
|
//
|
|
// If the ServiceType flag specifies that this is a kernel
|
|
// mode driver, raise a flag.
|
|
//
|
|
if (dwServiceType & SERVICE_KERNEL_DRIVER) {
|
|
VLOG(VLOG_LEVEL_INFO,
|
|
VLOG_KMODEDRIVER_INST,
|
|
"CreateServiceA was called. The path to the driver is %hs",
|
|
lpBinaryPathName);
|
|
}
|
|
}
|
|
|
|
return scHandle;
|
|
}
|
|
|
|
SC_HANDLE
|
|
APIHOOK(CreateServiceW)(
|
|
SC_HANDLE hSCManager, // handle to SCM database
|
|
LPCWSTR lpServiceName, // name of service to start
|
|
LPCWSTR lpDisplayName, // display name
|
|
DWORD dwDesiredAccess, // type of access to service
|
|
DWORD dwServiceType, // type of service
|
|
DWORD dwStartType, // when to start service
|
|
DWORD dwErrorControl, // severity of service failure
|
|
LPCWSTR lpBinaryPathName, // name of binary file
|
|
LPCWSTR lpLoadOrderGroup, // name of load ordering group
|
|
LPDWORD lpdwTagId, // tag identifier
|
|
LPCWSTR lpDependencies, // array of dependency names
|
|
LPCWSTR lpServiceStartName, // account name
|
|
LPCWSTR lpPassword // account password
|
|
)
|
|
{
|
|
SC_HANDLE scHandle;
|
|
|
|
scHandle = ORIGINAL_API(CreateServiceW)(hSCManager,
|
|
lpServiceName,
|
|
lpDisplayName,
|
|
dwDesiredAccess,
|
|
dwServiceType,
|
|
dwStartType,
|
|
dwErrorControl,
|
|
lpBinaryPathName,
|
|
lpLoadOrderGroup,
|
|
lpdwTagId,
|
|
lpDependencies,
|
|
lpServiceStartName,
|
|
lpPassword);
|
|
|
|
if (scHandle) {
|
|
//
|
|
// If the ServiceType flag specifies that this is a kernel
|
|
// mode driver, raise a flag.
|
|
//
|
|
if (dwServiceType & SERVICE_KERNEL_DRIVER) {
|
|
VLOG(VLOG_LEVEL_INFO,
|
|
VLOG_KMODEDRIVER_INST,
|
|
"CreateServiceW was called. The path to the driver is %ls",
|
|
lpBinaryPathName);
|
|
}
|
|
}
|
|
|
|
return scHandle;
|
|
}
|
|
|
|
/*++
|
|
|
|
Validate the registry data we received and warn the user if a driver is being installed.
|
|
|
|
--*/
|
|
void
|
|
WarnUserIfKernelModeDriver(
|
|
IN HANDLE hKey,
|
|
IN PUNICODE_STRING pstrValueName,
|
|
IN ULONG ulType,
|
|
IN PVOID pData
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
ULONG ulSize;
|
|
BYTE KeyNameInfo[MAX_INFO_LENGTH];
|
|
PKEY_NAME_INFORMATION pKeyNameInfo;
|
|
|
|
pKeyNameInfo = (PKEY_NAME_INFORMATION)KeyNameInfo;
|
|
|
|
//
|
|
// RegSetValue allows for NULL value names.
|
|
// Ensure that we don't have one before going any further.
|
|
//
|
|
if (!pstrValueName->Buffer) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Determine if the ValueName is 'Type'.
|
|
// If not, we don't need to go any further.
|
|
//
|
|
if (_wcsicmp(pstrValueName->Buffer, KMDI_VALUE_NAME)) {
|
|
DPFN(eDbgLevelInfo,
|
|
"[WarnUserIfKernelModeDriver] ValueName is not '%ls'",
|
|
KMDI_VALUE_NAME);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Determine if the type of the value is DWORD.
|
|
// If not, we need don't need to go any further.
|
|
//
|
|
if (REG_DWORD != ulType) {
|
|
DPFN(eDbgLevelInfo,
|
|
"[WarnUserIfKernelModeDriver] ValueType is not REG_DWORD");
|
|
return;
|
|
}
|
|
|
|
//
|
|
// At this point, we have a value that is of type REG_DWORD and
|
|
// has a name of 'Type'. Now we see if the key is a subkey of 'Services'.
|
|
//
|
|
status = NtQueryKey(hKey,
|
|
KeyNameInformation,
|
|
pKeyNameInfo,
|
|
MAX_INFO_LENGTH,
|
|
&ulSize);
|
|
|
|
if ((STATUS_BUFFER_OVERFLOW == status) ||
|
|
(STATUS_BUFFER_TOO_SMALL == status)) {
|
|
//
|
|
// Our stack based buffer wasn't large enough.
|
|
// Allocate from the heap and call it again.
|
|
//
|
|
pKeyNameInfo = (PKEY_NAME_INFORMATION)MemAlloc(ulSize);
|
|
|
|
if (!pKeyNameInfo) {
|
|
DPFN(eDbgLevelError,
|
|
"[WarnUserIfKernelModeDriver] Failed to allocate memory");
|
|
return;
|
|
}
|
|
|
|
status = NtQueryKey(hKey,
|
|
KeyNameInformation,
|
|
pKeyNameInfo,
|
|
ulSize,
|
|
&ulSize);
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// See if this key points to CurrentControlSet or ControlSet.
|
|
//
|
|
if (wcsistr(pKeyNameInfo->Name, KMDI_CURCONTROLSET_KEY) ||
|
|
wcsistr(pKeyNameInfo->Name, KMDI_CONTROLSET_KEY)) {
|
|
|
|
//
|
|
// Now see if this key points to Services.
|
|
//
|
|
if (wcsistr(pKeyNameInfo->Name, KMDI_SERVICES_KEY)) {
|
|
//
|
|
// We've got a key under 'Services'.
|
|
// If the Data has a value of 0x00000001,
|
|
// we've got a kernel mode driver being installed.
|
|
//
|
|
if ((*(DWORD*)pData == KMDI_TYPE_VALUE)) {
|
|
VLOG(VLOG_LEVEL_ERROR,
|
|
VLOG_KMODEDRIVER_INST,
|
|
"The driver was installed via the registry.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pKeyNameInfo != (PKEY_NAME_INFORMATION)KeyNameInfo) {
|
|
MemFree(pKeyNameInfo);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
APIHOOK(NtSetValueKey)(
|
|
IN HANDLE KeyHandle,
|
|
IN PUNICODE_STRING ValueName,
|
|
IN ULONG TitleIndex OPTIONAL,
|
|
IN ULONG Type,
|
|
IN PVOID Data,
|
|
IN ULONG DataSize
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
status = ORIGINAL_API(NtSetValueKey)(KeyHandle,
|
|
ValueName,
|
|
TitleIndex,
|
|
Type,
|
|
Data,
|
|
DataSize);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
WarnUserIfKernelModeDriver(KeyHandle, ValueName, Type, Data);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
SHIM_INFO_BEGIN()
|
|
|
|
SHIM_INFO_DESCRIPTION(AVS_KMODEDRIVER_DESC)
|
|
SHIM_INFO_FRIENDLY_NAME(AVS_KMODEDRIVER_FRIENDLY)
|
|
SHIM_INFO_VERSION(1, 1)
|
|
SHIM_INFO_FLAGS(AVRF_FLAG_EXTERNAL_ONLY)
|
|
SHIM_INFO_INCLUDE_EXCLUDE("I:advapi32.dll")
|
|
|
|
SHIM_INFO_END()
|
|
|
|
/*++
|
|
|
|
Register hooked functions
|
|
|
|
--*/
|
|
|
|
HOOK_BEGIN
|
|
|
|
DUMP_VERIFIER_LOG_ENTRY(VLOG_KMODEDRIVER_INST,
|
|
AVS_KMODEDRIVER_INST,
|
|
AVS_KMODEDRIVER_INST_R,
|
|
AVS_KMODEDRIVER_INST_URL)
|
|
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, CreateServiceA)
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, CreateServiceW)
|
|
APIHOOK_ENTRY(NTDLL.DLL, NtSetValueKey)
|
|
|
|
HOOK_END
|
|
|
|
IMPLEMENT_SHIM_END
|
|
|