USBDK/UsbDk/HiderStrategy.cpp
Dmitry Fleytman 1217ff2739 HiderStrategy: Re-use serial numbers of UsbDk devices
See log message of previous commit for rationale.

Signed-off-by: Dmitry Fleytman <dfleytma@redhat.com>
2017-02-19 14:10:30 +02:00

253 lines
8.8 KiB
C++

/**********************************************************************
* Copyright (c) 2013-2014 Red Hat, Inc.
*
* Developed by Daynix Computing LTD.
*
* Authors:
* Dmitry Fleytman <dmitry@daynix.com>
* Pavel Gurvich <pavel@daynix.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************/
#include "stdafx.h"
#include "HiderStrategy.h"
#include "trace.h"
#include "HiderStrategy.tmh"
#include "FilterDevice.h"
#include "ControlDevice.h"
#include "UsbDkNames.h"
NTSTATUS CUsbDkHiderStrategy::Create(CUsbDkFilterDevice *Owner)
{
auto status = CUsbDkNullFilterStrategy::Create(Owner);
if (NT_SUCCESS(status))
{
m_ControlDevice->RegisterHiddenDevice(*Owner);
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_HIDER, "%!FUNC! Serial number for this device is %lu", Owner->GetSerialNumber());
}
return status;
}
void CUsbDkHiderStrategy::Delete()
{
if (m_ControlDevice != nullptr)
{
m_ControlDevice->UnregisterHiddenDevice(*m_Owner);
}
CUsbDkNullFilterStrategy::Delete();
}
void CUsbDkHiderStrategy::PatchDeviceID(PIRP Irp)
{
static const WCHAR RedirectorDeviceId[] = L"USB\\Vid_2B23&Pid_CAFE&Rev_0001";
static const WCHAR RedirectorHardwareIds[] = L"USB\\Vid_2B23&Pid_CAFE&Rev_0001\0USB\\Vid_2B23&Pid_CAFE\0";
static const WCHAR RedirectorCompatibleIds[] = L"USB\\Class_FF&SubClass_FF&Prot_FF\0USB\\Class_FF&SubClass_FF\0USB\\Class_FF\0";
static const size_t MAX_DEC_NUMBER_LEN = 11;
WCHAR SzInstanceID[ARRAY_SIZE(USBDK_DRIVER_NAME) + MAX_DEC_NUMBER_LEN + 1];
const WCHAR *Buffer;
SIZE_T Size = 0;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_HIDER, "%!FUNC! for %!devid!", irpStack->Parameters.QueryId.IdType);
switch (irpStack->Parameters.QueryId.IdType)
{
case BusQueryDeviceID:
Buffer = &RedirectorDeviceId[0];
Size = sizeof(RedirectorDeviceId);
break;
case BusQueryInstanceID:
{
CString InstanceID;
auto status = InstanceID.Create(USBDK_DRIVER_NAME, m_Owner->GetSerialNumber());
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_HIDER, "%!FUNC! Failed to create instance ID string %!STATUS!", status);
return;
}
Size = InstanceID.ToWSTR(SzInstanceID, sizeof(SzInstanceID));
Buffer = &SzInstanceID[0];
break;
}
case BusQueryHardwareIDs:
Buffer = &RedirectorHardwareIds[0];
Size = sizeof(RedirectorHardwareIds);
break;
case BusQueryCompatibleIDs:
Buffer = &RedirectorCompatibleIds[0];
Size = sizeof(RedirectorCompatibleIds);
break;
default:
Buffer = nullptr;
break;
}
if (Buffer != nullptr)
{
auto Result = DuplicateStaticBuffer(Buffer, Size);
if (Result == nullptr)
{
return;
}
if (Irp->IoStatus.Information)
{
ExFreePool(reinterpret_cast<PVOID>(Irp->IoStatus.Information));
}
Irp->IoStatus.Information = reinterpret_cast<ULONG_PTR>(Result);
}
}
NTSTATUS CUsbDkHiderStrategy::PatchDeviceText(PIRP Irp)
{
static const WCHAR UsbDkDeviceText[] = USBDK_DRIVER_NAME L" device";
const WCHAR *Buffer = nullptr;
SIZE_T Size = 0;
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_HIDER, "%!FUNC! Entry");
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
switch (irpStack->Parameters.QueryDeviceText.DeviceTextType)
{
case DeviceTextDescription:
Buffer = &UsbDkDeviceText[0];
Size = sizeof(UsbDkDeviceText);
break;
default:
break;
}
if (Buffer != nullptr)
{
auto Result = DuplicateStaticBuffer(Buffer, Size);
if (Result != nullptr)
{
if (Irp->IoStatus.Information != 0)
{
ExFreePool(reinterpret_cast<PVOID>(Irp->IoStatus.Information));
}
Irp->IoStatus.Information = reinterpret_cast<ULONG_PTR>(Result);
Irp->IoStatus.Status = STATUS_SUCCESS;
}
}
return Irp->IoStatus.Status;
}
bool CUsbDkHiderStrategy::ShouldDenyRemoval() const
{
// There is a phenomena reported for Windows 7 32/64 bits related
// to hidden UsbDk devices that do not have cached driver information.
//
// Upon such a device appearance Windows correctly decides to assign
// the NULL driver to this device, creates correct cached driver information
// and right after that, for some reason, destructs driver stack for this
// device PDO and UsbDk filter gets removed.
//
// Having driver stack destroyed, Windows, apparently, queries device IDs
// again without UsbDk filter attached and gets unmodified information
// as supplied by bus driver.
//
// After that Windows builds driver stack again, including attachment of
// UsbDk filter and loads device driver according to IDs obtained on
// previous step when UsbDk filter was not loaded.
//
// This sequence effectively cancels all UsbDk efforts
// and renders the device visible.
//
// Our experiments show, that in case hidden device turns down Windows
// request for unload right after driver stack creation, Windows gives up
// do not try to rebuild driver stack for this device.
//
// Experiments also show that Windows may request device unload more than
// once, so just failing the first request is not enough, therefore this
// function implements a timer-based logic for turning down early
// unload requests.
static const ULONG REMOVAL_DENIAL_TIMEOUT_MS = 3000;
auto TimePassedSinceCreation = m_RemovalStopWatch.TimeMs();
if (TimePassedSinceCreation <= REMOVAL_DENIAL_TIMEOUT_MS)
{
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_HIDER,
"%!FUNC! Denying removal because %llu ms only passed since creation",
TimePassedSinceCreation);
return true;
}
else
{
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_HIDER,
"%!FUNC! Allowing removal because %llu ms already passed since creation",
TimePassedSinceCreation);
return false;
}
}
NTSTATUS CUsbDkHiderStrategy::PNPPreProcess(PIRP Irp)
{
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
switch (irpStack->MinorFunction)
{
case IRP_MN_QUERY_ID:
return PostProcessOnSuccess(Irp,
[this](PIRP Irp) -> NTSTATUS
{
PatchDeviceID(Irp);
return STATUS_SUCCESS;
});
case IRP_MN_QUERY_CAPABILITIES:
return PostProcessOnSuccess(Irp,
[](PIRP Irp) -> NTSTATUS
{
auto irpStack = IoGetCurrentIrpStackLocation(Irp);
irpStack->Parameters.DeviceCapabilities.Capabilities->RawDeviceOK = 1;
irpStack->Parameters.DeviceCapabilities.Capabilities->NoDisplayInUI = 1;
irpStack->Parameters.DeviceCapabilities.Capabilities->Removable = 0;
irpStack->Parameters.DeviceCapabilities.Capabilities->EjectSupported = 0;
irpStack->Parameters.DeviceCapabilities.Capabilities->SilentInstall = 1;
return STATUS_SUCCESS;
});
case IRP_MN_QUERY_DEVICE_TEXT:
return PostProcess(Irp,
[this](PIRP Irp, NTSTATUS Status) -> NTSTATUS
{
UNREFERENCED_PARAMETER(Status);
return PatchDeviceText(Irp);
});
case IRP_MN_QUERY_REMOVE_DEVICE:
return ShouldDenyRemoval() ? CompleteWithStatus(Irp, STATUS_DEVICE_BUSY)
: CUsbDkNullFilterStrategy::PNPPreProcess(Irp);
default:
return CUsbDkNullFilterStrategy::PNPPreProcess(Irp);
}
}