Однажды примерно такая задача и стояла передо мной. Времени на поиск каких-либо решений в Интернете не было, да и они, как правило, небесплатные. Поэтому мне было проще написать такой драйвер, и его реализация отняла у меня один день.
В этой статье я расскажу немного теоретическую часть, на основе которой все строится, и расскажу принцип самого решения.
Также полные исходные коды могут быть найдены в папке USBLock хранилища git по адресу: https://github.com/anatolymik/samples.git.
Структура DRIVER_OBJECT
Для каждого загруженного драйвера система формирует структуру DRIVER_OBJECT. Этой структурой система активно пользуется, когда отслеживает состояние драйвера. Также драйвер отвечает за ее инициализацию, в частности за инициализацию массива MajorFunction. Этот массив содержит адреса обработчиков для всех запросов, за которые драйвер может отвечать. Следовательно, когда система будет посылать запрос драйверу, она воспользуется этим массивом, чтобы определить, какая функция драйвера отвечает за конкретный запрос. Ниже представлен пример инициализации этой структуры.
for ( ULONG i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++ ) {
DriverObject->MajorFunction[i] = DispatchCommon;
}
DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchClose;
DriverObject->MajorFunction[IRP_MJ_READ] = DispatchRead;
DriverObject->MajorFunction[IRP_MJ_WRITE] = DispatchWrite;
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = DispatchCleanup;
DriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnp;
DriverObject->DriverUnload = DriverUnload;
DriverObject->DriverExtension->AddDevice = DispatchAddDevice;
Такая инициализация обычно выполняется при вызове системой точки входа драйвера, прототип которой изображен ниже.
NTSTATUS DriverEntry( PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath );
Как видно из примера, сначала весь массив MajorFunction инициализируется одним и тем же обработчиком. В реальности типов запросов больше, чем в примере. Поэтому предварительно весь массив инициализируется так, чтобы запросы, которые не поддерживаются драйвером, обрабатывались корректно. Например, завершались с ошибкой. После инициализации массива обычно инициализируются обработчики для тех запросов, за которые драйвер отвечает.
Также инициализируется поле DriverUnload структуры, которое содержит адрес обработчика, отвечающего за завершение работы драйвера. Это поле может быть оставлено непроинициализированным, в таком случае драйвер становится невыгружаемым.
Обратите внимание на то, что в поле DriverExtension->AddDevice устанавливается адрес обработчика, который вызывается всякий раз, когда система обнаруживает новое устройство, за работу которого драйвер отвечает. Данное поле может быть оставлено непроинициализированным, в таком случае драйвер не сможет обрабатывать это событие.
Более подробно данная структура описана по адресу: https://msdn.microsoft.com/en-us/library/windows/hardware/ff544174(v=vs.85).aspx.
Структура DEVICE_OBJECT
Структура DEVICE_OBJECT представляет ту или иную функциональность драйвера. Т.е. эта структура может представлять физическое устройство, логическое устройство, виртуальное устройство или просто некий функционал, предоставляемый драйвером. Поэтому когда система будет посылать запросы, то в самом запросе она будет указывать адрес этой структуры. Таким образом, драйвер сможет определить, какой функционал от него запрашивается. Если не использовать такую модель, тогда драйвер может обрабатывать только какую-нибудь одну функциональность, а в современном мире это недопустимо. Прототип функции, которая обрабатывает конкретный запрос, приведена ниже.
NTSTATUS Dispatch( PDEVICE_OBJECT DeviceObject, PIRP Irp );
Массив MajorFunction ранее упомянутой структуры DRIVER_OBJECT содержит адреса обработчиков именно с таким прототипом.
Сама структура DEVICE_OBJECT всегда создается драйвером при помощи функции IoCreateDevice. Если система посылает запрос драйверу, то она всегда направляет его какому-либо DEVICE_OBJECT, как это следует из вышепредставленного прототипа. Также, прототип принимает второй параметр, который содержит адрес IRP-структуры. Эта структура описывает сам запрос, и она существует в памяти до тех пор, пока драйвер не завершит его. Запрос отправляется драйверу на обработку при помощи функции IoCallDriver как системой, так и другими драйверами.
Также со структурой DEVICE_OBJECT может быть связано имя. Таким образом, этот DEVICE_OBJECT может быть найден в системе.
Более подробно структура DEVICE_OBJECT описана по адресу: https://msdn.microsoft.com/en-us/library/windows/hardware/ff543147(v=vs.85).aspx. А структура IRP описана по адресу: https://msdn.microsoft.com/en-us/library/windows/hardware/ff550694(v=vs.85).aspx.
Фильтрация
Фильтрация являет собой механизм, который позволяет перехватывать все запросы, направленные к конкретному DEVICE_OBJECT. Чтобы установить такой фильтр, необходимо создать другой экземпляр DEVICE_OBJECT и прикрепить его к DEVICE_OBJECT, запросы которого необходимо перехватывать. Прикрепление фильтра выполняется посредством функции IoAttachDeviceToDeviceStack. Все DEVICE_OBJECT, прикрепленные к перехватываемому DEVICE_OBJECT, вместе с ним формируют так называемый стек устройства, как это изображено ниже.
Стрелкой изображено продвижение запроса. Сначала запрос будет обрабатываться драйвером верхнего DEVICE_OBJECT, затем драйвером среднего и, в конце концов, управление на обработку запроса получит драйвер целевого DEVICE_OBJECT. Также нижний DEVICE_OBJECT называется дном стека, т.к. он ни к кому не прикреплен.
Наличие такого механизма позволяет добавлять функционал, которого нет изначально в драйверах. Например, таким образом, без доработки файловой системы FAT, поставляемой в Windows, можно добавить проверку прав доступа к файлам этой файловой системы.
PnP менеджер
PnP менеджер отвечает за диспетчеризацию устройств всей системы. В его задачи входит обнаружение устройств, сбор информации о них, загрузка их драйверов, вызов этих драйверов, управление аппаратными ресурсами, запуск и остановка устройств и их удаление.
Когда драйвер той или иной шины обнаруживает устройства на своих интерфейсах, то для каждого дочернего устройства он создает DEVICE_OBJECT. Этот DEVICE_OBJECT также называют Physical Device Object или PDO. Затем посредством функции IoInvalidateDeviceRelations он уведомляет PnP менеджер о том, что произошли изменения на шине. В ответ на это PnP менеджер посылает запрос с minor кодом IRP_MN_QUERY_DEVICE_RELATIONS с целью запросить список дочерних устройств. В ответ на этот запрос драйвер шины возвращает список PDO. Ниже изображен пример такой ситуации.
Как отражено на рисунке, в данном примере в качестве шины выступает драйвер USB-хаба. Конкретные DEVICE_OBJECT стека устройства этого хаба не изображены для краткости и с целью сохранения последовательности пояснений.
Как только PnP менеджер получит список всех PDO, он по отдельности соберет всю необходимую информацию об этих устройствах. Например, будет послан запрос с minor кодом IRP_MN_QUERY_ID. Посредством этого запроса PnP менеджер получит идентификаторы устройства, как аппаратные, так и совместимые. Также PnP менеджер соберет всю необходимую информацию о требуемых аппаратных ресурсах самим устройством. И так далее.
После того, как вся необходимая информация будет собрана, PnP менеджер создаст так называемую DevNode, которая будет отражать состояние устройства. Также PnP создаст ветку реестра для конкретного экземпляра устройства или откроет существующую, если устройство ранее подключалось к ПК.
Следующая задача PnP — это запуск драйвера устройства. Если драйвер не был ранее установлен, тогда PnP будет ожидать установки. Иначе, при необходимости, PnP загрузит его и передаст ему управление. Ранее упоминалось, что поле DriverExtension->AddDevice структуры DRIVER_OBJECT содержит адрес обработчика, который вызывается всякий раз, когда система обнаруживает новое устройство. Прототип этого обработчика изображен ниже.
NTSTATUS DispatchAddDevice(
PDRIVER_OBJECT DriverObject,
PDEVICE_OBJECT PhysicalDeviceObject
);
Т.е. всякий раз, когда PnP обнаруживает устройство, управлением которого занимается тот или иной драйвер, вызывается зарегистрированный обработчик этого драйвера, где ему передается указатель на PDO. Информация об установленном драйвере также хранится в соответствующей ветке реестра.
В задачу обработчика входит создание DEVICE_OBJECT и его прикрепление к PDO. Прикрепленный DEVICE_OBJECT также называют Functional Device Object или FDO. Именно этот FDO и будет отвечать за работу устройства и представление его интерфейсов в системе. Ниже представлен пример, когда PnP завершил вызов драйвера, отвечающего за работу устройства.
Как отражено на примере, кроме драйвера самого устройства также могут быть зарегистрированы нижние и верхние фильтры класса устройства. Следовательно, если таковые имеются, PnP также загрузит их драйвера и вызовет их AddDevice обработчики. Т.е. порядок вызова драйверов следующий: сначала загружаются и вызываются зарегистрированные нижние фильтры, затем загружается и вызывается драйвер самого устройства, и в завершении загружаются и вызываются верхние фильтры. Нижние и верхние фильтры являются обычным DEVICE_OBJECT, которые создают драйвера и прикрепляют их к PDO в своих обработчиках AddDevice. Количество нижних и верхних фильтров не ограничено.
В этот момент стеки устройств полностью сформированы и готовы к работе. Поэтому PnP посылает запрос с minor кодом IRP_MN_START_DEVICE. В ответ на этот запрос все драйвера стека устройства должны подготовить устройство к работе. И если в этом процессе не возникло проблем, тогда запрос завершается успешно. В противном случае, если любой из драйверов не может запустить устройство, тогда он завершает запрос с ошибкой. Следовательно, устройство не будет запущено.
Также, когда драйвер шины определяет, что произошли изменения на шине, он посредством функции IoInvalidateDeviceRelations уведомляет PnP о том, что следует заново собрать информацию о подключенных устройствах. В этот момент драйвер не удаляет ранее созданный PDO. Просто при получении запроса с minor кодом IRP_MN_QUERY_DEVICE_RELATIONS он не включит этот PDO в список. Затем PnP на основании полученного списка опознает новые устройства и устройства, которые были отключены от шины. PDO отключенных устройств драйвер удалит тогда, когда PnP пошлет запрос с minor кодом IRP_MN_REMOVE_DEVICE. Для драйвера этот запрос означает, что устройство более никем не используется, и оно может быть безопасно удалено.
Более подробную информацию о модели драйверов WDM можно найти по адресу: https://msdn.microsoft.com/en-us/library/windows/hardware/ff548158(v=vs.85).aspx.
Суть решения
Суть самого решения заключается в создании верхнего фильтра класса USB-шины. Зарезервированные классы можно найти по адресу: https://msdn.microsoft.com/en-us/library/windows/hardware/ff553419(v=vs.85).aspx. Нас интересует класс USB с GUID равным 36fc9e60-c465-11cf-8056-444553540000. Как гласит MSDN, этот класс используется для USB хост контроллеров и хабов. Однако практически это не так, этот же класс используется, например, flash-накопителями. Это немного добавляет нам работы. Код обработчика AddDevice представлен ниже.
NTSTATUS UsbCreateAndAttachFilter(
PDEVICE_OBJECT PhysicalDeviceObject,
bool UpperFilter
) {
SUSBDevice* USBDevice;
PDEVICE_OBJECT USBDeviceObject = nullptr;
ULONG Flags;
NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
for ( ;; ) {
// если нижний фильтр уже прикреплен, тогда здесь больше делать нечего
if ( !UpperFilter ) {
USBDeviceObject = PhysicalDeviceObject;
while ( USBDeviceObject->AttachedDevice ) {
if ( USBDeviceObject->DriverObject == g_DriverObject ) {
return STATUS_SUCCESS;
}
USBDeviceObject = USBDeviceObject->AttachedDevice;
}
}
// создаем фильтр
Status = IoCreateDevice(
g_DriverObject,
sizeof( SUSBDevice ),
nullptr,
PhysicalDeviceObject->DeviceType,
PhysicalDeviceObject->Characteristics,
false,
&USBDeviceObject
);
if ( !NT_SUCCESS( Status ) ) {
break;
}
// инициализируем флаги созданного устройства, копируем их из объекта к
// которому прикрепились
Flags = PhysicalDeviceObject->Flags &
(DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE);
USBDeviceObject->Flags |= Flags;
// получаем указатель на нашу структуру
USBDevice = (SUSBDevice*)USBDeviceObject->DeviceExtension;
// инициализируем деструктор
USBDevice->DeleteDevice = DetachAndDeleteDevice;
// инициализируем обработчики
for ( ULONG i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++ ) {
USBDevice->MajorFunction[i] = UsbDispatchCommon;
}
USBDevice->MajorFunction[IRP_MJ_PNP] = UsbDispatchPnp;
USBDevice->MajorFunction[IRP_MJ_POWER] = UsbDispatchPower;
// инициализируем семафор удаления устройства
IoInitializeRemoveLock(
&USBDevice->Lock,
USBDEVICE_REMOVE_LOCK_TAG,
0,
0
);
// заполняем структуру
USBDevice->SelfDevice = USBDeviceObject;
USBDevice->BaseDevice = PhysicalDeviceObject;
USBDevice->UpperFilter = UpperFilter;
// инициализируем paging семафор
USBDevice->PagingCount = 0;
KeInitializeEvent( &USBDevice->PagingLock, SynchronizationEvent, true );
// прикрепляем устройство к PDO
USBDevice->LowerDevice = IoAttachDeviceToDeviceStack(
USBDeviceObject,
PhysicalDeviceObject
);
if ( !USBDevice->LowerDevice ) {
Status = STATUS_NO_SUCH_DEVICE;
break;
}
break;
}
// в зависимости от результата делаем
if ( !NT_SUCCESS( Status ) ) {
// отчистку
if ( USBDeviceObject ) {
IoDeleteDevice( USBDeviceObject );
}
} else {
// или сбрасываем флаг инициализации
USBDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
}
return Status;
}
static NTSTATUS DispatchAddDevice(
PDRIVER_OBJECT DriverObject,
PDEVICE_OBJECT PhysicalDeviceObject
) {
UNREFERENCED_PARAMETER( DriverObject );
return UsbCreateAndAttachFilter( PhysicalDeviceObject, true );
}
Как следует из примера, мы создаем DEVICE_OBJECT и прикрепляем его к PDO. Таким образом, мы будем перехватывать все запросы, направленные к USB-шине.
В нашу задачу входит перехватывать запросы с minor кодом IRP_MN_START_DEVICE. Код обработчика этого запроса изображен ниже.
static NTSTATUS UsbDispatchPnpStartDevice( SUSBDevice* USBDevice, PIRP Irp ) {
bool HubOrComposite;
NTSTATUS Status;
PAGED_CODE();
for ( ;; ) {
// проверить, позволено ли устройству работать, также обновить
// информацию об устройстве, является ли оно хабом или композитным
Status = UsbIsDeviceAllowedToWork( &HubOrComposite, USBDevice );
if ( !NT_SUCCESS( Status ) ) {
break;
}
USBDevice->HubOrComposite = HubOrComposite;
// продвинуть запрос
Status = ForwardIrpSynchronously( USBDevice->LowerDevice, Irp );
if ( !NT_SUCCESS( Status ) ) {
break;
}
break;
}
// завершаем запрос
Irp->IoStatus.Status = Status;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
// и освобождаем устройство
IoReleaseRemoveLock( &USBDevice->Lock, Irp );
return Status;
}
Как изображено на рисунке, обработчик вызывает функцию UsbIsDeviceAllowedToWork. Эта функция выполняет все необходимые проверки, чтобы определить, разрешено ли устройству работать. В первую очередь функция позволяет всегда работать хабам и композитным устройствам, клавиатурам и мышам. А также тем устройствам, которые находятся в списке разрешенных. Если функция возвращает неуспешный код возврата, тогда запрос завершается с ошибкой. Таким образом, работа устройства будет заблокирована.
Обратите внимание: функция определяет, является ли устройство хабом или композитным устройством. Это необходимо потому, что, как уже было упомянуто, класс устройств, который используется для хабов и хост контроллеров, используется не только этими устройствами. А нам в первую очередь необходимо контролировать дочерние устройства только хабов, хост контроллеров и композитных устройств. Т.е. для хабов и композитных устройств дополнительно перехватывается запрос перечисления дочерних устройств, на этом этапе, важно также прикрепить ко всем дочерним устройствам фильтр, и этот фильтр будет нижним. В противном случае контроль над дочерними устройствами будет потерян.
Все упомянутые определения выполняются на основе идентификаторов устройств.
Заключение
Несмотря на свою простоту в моем случае данный драйвер достаточно эффективно решает поставленную задачу. Хотя из недостатков следует выделить обязательную перезагрузку после того, как список разрешенных устройств будет обновлен. Чтобы устранить этот недостаток, драйвер потребуется несколько усложнить. Еще большим недостатком является полное блокирование устройства, а не частичное. Описание, представленное выше, не раскрывает всех деталей реализации. Сделано это было намеренно, и упор был сделан больше на саму концепцию. Желающие разобраться во всем до конца могут ознакомиться с исходным кодом.
Комментарии (86)
gjf
14.06.2017 12:42-8Автор начал с того, что существующие решения небесплатные, а затем сваял драйвер, который ещё нужно дорабатывать и — о чудо! — который нужно небесплатно подписать, чтобы он заработал на современных ОС.
Я ничего не упустил?anatolymik
14.06.2017 12:55+6Мне казалось, что заголовок гласит о написании, а не предложении абсолютно бесплатного решения.
О каких современных ОС идет речь? Драйвер работает на всех ОС.gjf
14.06.2017 12:59-8Любая статья предполагает обоснование необходимости.
Если кодили just for fun — ок, но только Ваш fun малоинтересен. Уровня задачки начального курса программирования на уровне ядра. Да и справились Вы так себе — да и сами написали, что ещё пилить и пилить.
Попробуйте использовать свой драйвер на любой поддерживаемой в настоящий момент 64-разрядной ОС Windows.anatolymik
14.06.2017 13:05+5Использую, работает. Прежде чем утверждать подобное стоило бы проверить.
Задача не just for fun, она вышла из практики, была реальная потребность. Если вам этот fun не интересен, поправьте, но по-моему, вас никто не заставляет давать какие-либо комментарии на этот счет. Если не интересно, зачем вообще читать?
Касательно начального курса программирования, я могу назвать множество людей, которые работают в этой области, и которые этой простой задачи сделать не могут.
Справился безусловно так себе.
А вообще, вы просто передергиваете мои слова, тем самым меняя смысл.gjf
14.06.2017 13:10-8Если задача из жизни, которую Вы не хотите, чтобы обсуждали — зачем о ней здесь писать? Хабр предполагает конструктивную критику, а не только восторженные вопли.
Вы пишите, что была реальная необходимость, и Вы задачу решили — эээ, в Вашей компании нет ни одного компьютера под управлением 64-разрядной ОС позже Windows 7 включительно? Вы так и не услышали: без подписи на этих системах драйвер работать не будет! Так в чём тогда интересность и ценность Вашего решения?anatolymik
14.06.2017 13:17+2Например, вы заблокировали доступ к Интернету одному из пользователей, но не заблокировали устройства этого ПК. В таком случае пользователю достаточно просто принести USB-модем, и Интернет у него будет. Т.е. простым блокированием доступа к Интернету дело не ограничивается.
В самом начале, я написал практическую потребность, извиняюсь если написал размыто.
Вы пишите, что была реальная необходимость, и Вы задачу решили — эээ, в Вашей компании нет ни одного компьютера под управлением 64-разрядной ОС позже Windows 7 включительно?
Я драйвер подписываю.
Несмотря на свою простоту в моем случае данный драйвер достаточно эффективно решает поставленную задачу.
Я явно сказал что в моем случае.
Вы так и не услышали: без подписи на этих системах драйвер работать не будет!
Еще раз, я писал о разработке, а не об обходе проверки подписи.
Так в чём тогда интересность и ценность Вашего решения?
Решать вам, и остальным читателям.gjf
14.06.2017 13:22-8То есть у Вас или Вашей компании есть деньги на сертификат для подписи драйвера, но нет денег на решение проблемы подключения сторонних устройств?
И в итоге специалист, имеющий доступ к сертификату, целый день тратит на написание драйвера, который к тому же потом требует доработки?
Извините, но если бы Вы работали в моей компании, после этого я бы Вас уволил.
Но Вы правы: всегда может быть иное мнение. Посмотрим, что скажут другие.
Для себя я всё понял, спасибо за диалог.Roman_Cherkasov
14.06.2017 13:29+4Не кажется глупым решением уволить человека, который справился с задачей, без дополнительных трат и проприетарного ПО? И потом искать замену? Странное решение на мой скромный взгляд.
gjf
14.06.2017 13:37-10Если решение на уровне учителя информатики в школе — тогда да. Но тут, как видите, не тот уровень.
Специалист на работе должен заниматься работой. Его рабочий день стоит дороже софта для блокировки.
Кроме того, если за день он не успел отладить простой драйвер до состояния «больше недостатков нет» — тогда он точно не на своём месте.
HonoraryBoT
14.06.2017 14:34+5Не подскажете название вашей компании? Просто на заметку.
janekprostojanek
14.06.2017 15:48+3Да нет у него никакой компании… И в компанию автора на место автора его бы не взяли
anatolymik
14.06.2017 15:51+2Может и есть, просто он там один, на должности генерального директора. Увольняет всех.
gjf
14.06.2017 21:20-3Извините, вакансий нет. Спросите автора статьи — судя по всему, работа у них не пыльная.
anatolymik
14.06.2017 21:30+1Ну да, не пыльная. С киркой и отбойным молотком ходить не надо. Кнопочки там всякие нажимаю.
dartraiden
15.06.2017 06:41Во-первых, проверка подписи отключается в Windows штатно, во-вторых, не секрет (вы это точно знаете), что в сети гуляет утилита, позволяющая подписать особым просроченным сертификатом любой драйвер, а MS закрыть уязвимость, используемую этим сертификатом не может, предпочитая игнорировать проблему.
gjf
15.06.2017 07:14-1Штатно — это перевод в тестовый режим. Слово «тестовый» Вам ничего не говорит? Да, на своём личном компьютере я для разработки могу включить такой режим, могу отключить UAC и постоянно работать под админом — но это не есть решение для рабочих станций, где необходима нормальная работа ОС и нормальный, штатный уровень безопасности.
«Утилита в сети» — это украденый сертификат с китайской программой для подписи.
Ребята, мы здесь обсуждаем решения для компании или пиратские обходы дома на завалинке? Давайте мухи — отдельно, котлеты — отдельно.
WRP
15.06.2017 09:07А можете дать ссылку на эту утилиту с просроченным сертификатом или более полную информацию? Спасибо.
gdt
14.06.2017 15:08Спасибо, очень интересно. На прошлой работе стояла похожая задача — надо было запретить подключение к определённому usb хабу всех устройств, кроме usb накопителей. Т. к. это было ПО для терминалов, я просто сделал перехват сообщений WM_DEVICECHANGE, и принудительно отрубал все устройства с требуемого хаба, которые не являются usbstorage.
anatolymik
14.06.2017 15:10Вырубали через SetupAPI?
gdt
14.06.2017 16:24Насколько я помню, да, давненько уже было. В крайнем случае можно devcon'ом рубить.
anatolymik
14.06.2017 16:48О таком решении я и не думал. В целом хорошее. Но в моем случае оно бы не подошло.
HonoraryBoT
14.06.2017 21:38У такого решения есть проблема с открытыми хэндлами. SetupApi не отключит девайс с открытыми хэндлами, а устройство (та же флэшка) может, например, быть занята при запущенном приложении автозапуска. До появления уведомления о новом устройстве можно успеть много чего на нем открыть. Вариант — перебирать хэндлы и закрывать их вручную, что не очень хорошо сказывается на стабильности работы приложений.
gdt
16.06.2017 19:47Не понимаю, о каких открытых хэндлах в данном случае идёт речь, если честно. Сообщение WM_DEVICECHANGE передаётся до тех пор, пока не обработается. Чисто теоретически, на замусоренной системе, кто-то может успеть его обработать раньше. Но тогда моя программа его не получит. Или мы говорим о разных вещах.
P.S. в моём конкретном случае было допустимо рубить устройства devcon'ом (издержки производства, иногда, например, на уже поставленных терминалах отходит питание от купюроприёмника, и devcon — единственный способ программно вернуть устройство в рабочее состояние).
redmanmale
14.06.2017 15:29+1Безотносительно кода, решение весьма странное.
Если у пользователя нет прав администратора, то задачу можно решить проще, стандартными средствами контроля доступа, а если права есть, то он может просто использовать обычный драйвер, разве нет?anatolymik
14.06.2017 15:32+1Приведите пример стандартных средств.
Saymon
14.06.2017 16:10Извините, я давно usb-свистками не пользовался, но разве они уже могут устанавливать свои драйвера и софт, который требуется им для работы, без прав админа?
anatolymik
14.06.2017 16:11Ну тут разграничение прав, админ ставит софт, пользователь использует
Saymon
14.06.2017 16:180_о А зачем админу сначала ставить софт для работы usb-модема, а затем писать драйвер, чтобы этот usb-модем не мог работать?
anatolymik
14.06.2017 16:22+1А дело только в модемах? Во-вторых, много драйверов в современных Windows предустановлены.
Saymon
14.06.2017 16:34Пример то был с модемом. И очевидно, что он не очень удачный. На моей практике, когда пользователи пытались подобным образом обойти блокировку на развлекательные ресурсы, то они обломались.Операционные системы win-7, win-8 на тот момент были.
anatolymik
14.06.2017 16:40У USB устройств есть классы, у этих классов устройств часто бывает стандартный интерфейс. Для этого стандартного интерфейса есть предустановленный драйвер в системе. И?
Saymon
14.06.2017 16:48Вы на практике сталкивались с тем, чтобы удалось использовать usb-модем без установки дополнительного ПО?
anatolymik
14.06.2017 16:51Именно так. Не надо только путать современные модемы, с модемами времен Windows XP.
Еще раз, для стандартного класса USB устройства в системе может присутствовать драйвер. Сейчас сетевые адаптеры есть работающие по USB. И никакого спец. софта для них не нужно.
Roman_Cherkasov
22.06.2017 10:54Ни один Yota модем, которыми я к сожалению — вынужден с завидной периодичностью пользоваться — не устанавливает никакие драйвера. Windows 7 — 10. На XP говорят приходится что то ставить, но к сожалению возможности проверить — нет.
redmanmale
14.06.2017 16:47Возможно я не прав, но чтобы модем заработал, ему надо создать новое подключение (обычно это делает ПО модема при установке).
Без прав администратора пользователь во-первых, не сможет установить драйвер модема, а во-вторых, если драйвер уже есть и не нужен, не сможет создать новое сетевое подключение.
В крайнем случае, можно, например, явно запретить пользователю создание новых подключений через групповые политики.
anatolymik
14.06.2017 16:49В том и прелесть, что люди подключили модем без прав админа.
Saymon
14.06.2017 16:54А можете модель модема озвучить?
anatolymik
14.06.2017 16:55Не могу, знаю только сам факт. Т.к. ИТ'шники обращались с такой просьбой.
Saymon
14.06.2017 17:09+1Тогда не факт, что это факт. Вполне вероятно либо прошляпили админский доступ, либо юзер оказался достаточно продвинутым и упорным, чтобы ломануть учетку локального админа, например включил встроенного администратора. Беглый поиск по интернету не выявил модель usb-модема, которую можно установить без прав админа. Так что вполне вы занимались написание велосипеда из за не отлаженных должным образом политик и регламентов.Если пользователь установивший вопреки правилам модем не понес никакого наказания, и не было подробного расследования для выявления факта халатности админов, где гарантия, что тот же самый юзер в следующий раз не получит снова права админа и не удалит из системы этот самописный драйвер?
anatolymik
14.06.2017 17:13Я написал, а ваше дело мне верить или нет. Есть люди которым вы верите, есть которым я.
Я еще раз подчеркну, в Windows предустановленных драйверов огромная масса. Полагаться на то что устройство потребует особой установки, не очень верно. Т.к. не дает максимальной гарантии.
P.S. На счет наказаний немного ставит в ступор. Вы встречали когда-нибудь человека которого всегда наказывали только по делу?Saymon
14.06.2017 17:34А в чем ступор? Первая превентивная мера защиты от несанкционированного доступа это письменное уведомление о санкциях за этот доступ под роспись. Выпуск соответствующего документа это первое, что я прошу от руководства клиента, когда они хотят в чем то ограничить пользователей. Как я и указал выше вопрос правильных политик и регламентов.
И лично я не ставлю под сомнение лично Вашу честность. Но вот сама предыстория вызывает сомнения.
Главную мысль, которую я пытаюсь озвучить, что не нужно начинать искать решение задач администрирования чисто техническими средствами.anatolymik
14.06.2017 17:42+1Как показывает практика, в регламентах любой компании всегда есть дыры. Если цель ходить и заниматься справедливостью, тогда да. Хороший подход. Если ликвидировать вариант угрозы на корню, тогда технически. Пусть техника думает. Человеческий фактор никто не отменял. Да и ходить и заниматься воспитанием людей не самое интересное занятие. Лучше, если они не будут иметь технической возможности.
Saymon
14.06.2017 17:52+1Вы не поняли. Вопрос не в абстрактной справедливости, а в рациональности. Где гарантия что, использование вашего решения по обходу подписи драйверов не нанесет значительный урон безопасности? Или что не создаст проблем при подключении устройств, драйвера к которому писали «индусы», но вот именно их и нужно подключить именно к этому рабочему месту? Конечно надежней запретить использование металлических кухонных ножей, но заставлять всех использовать одноразовые пластиковые не рационально.
anatolymik
14.06.2017 17:56Вы не поняли. Вопрос не в абстрактной справедливости, а в рациональности. Где гарантия что, использование вашего решения по обходу подписи драйверов не нанесет значительный урон безопасности?
В каком месте я обхожу подпись драйверов?
Или что не создаст проблем при подключении устройств, драйвера к которому писали «индусы», но вот именно их и нужно подключить именно к этому рабочему месту?
С таким подходом лучше тогда вообще ничего не делать, т.к. любое решение может нанести вред. Какие проблемы может породить фильтрация обычных запросов PnP, который писала Microsoft?
Конечно надежней запретить использование металлических кухонных ножей, но их заставлять всех использовать не рационально.
По-моему, вы перевернули все с ног на голову.
Saymon
14.06.2017 18:18В каком месте я обхожу подпись драйверов?
За это извиняюсь, про обход подписи это был не ваш комментарий.
Какие проблемы может породить фильтрация обычных запросов PnP, который писала Microsoft?
Вам разве не приходилось еще сталкиваться с тем что «это не должно приводить к проблемам» не обязательно означает, что «это действительно не привело к проблемам». Особенно когда дело касается Microsoft. :)
По-моему, вы перевернули все с ног на голову.
там опечатка была, поэтому фраза действительно получилась бессмысленной. Возможно Вам приходилось читать притчу «про хакера и начальника столовой»?
Ну опять же вернусь к тому, что Ваше решение в итоге не защищает от того, кто будет специально искать как его обойти\сломать, т.к. нужно не только совершенствовать замки, но и ловить взломщиков.
anatolymik
14.06.2017 18:33+1Вам разве не приходилось еще сталкиваться с тем что «это не должно приводить к проблемам» не обязательно означает, что «это действительно не привело к проблемам». Особенно когда дело касается Microsoft. :)
Я много с чем сталкивался. И Microsoft последняя в списке. Статистически. Вообще когда говорят «особенно Microsoft» — все становиться ясно.
Ну опять же вернусь к тому, что Ваше решение в итоге не защищает от того, кто будет специально искать как его обойти\сломать
Ну сломайте kernel mode драйвер. Обойдите его механизмы. Вы сейчас сказали в целом правильную вещь, но бессмысленную. Т.к. из такого утверждения следует что не надо ничего писать, потому что это сломают.
gjf
14.06.2017 21:18-5Сдаётся мне, что отсутствие модели модема, отсутствие понимания автором защиты драйверов подписью, откровенно высосанные из пальца детали истории, отсутствие исходников и скомпилированного детища, а также активность на Хабре в рабочее время намекает, что статья — копипаста, не имеющая ничего общего с реальностью.
В принципе, согласен в этом с Ivan_83.
VBKesha
14.06.2017 22:17Я очень давно не видел этих модемов, но те что видел лет 5 назад были обычно составными устройствами состоящими из диска с дрова и ком порта(CDC). иногда с возможность работы как USB сетевуха. Многие из них требовали спец софт для активации CDC/Сетевухи. Однако их можно было потом заставить остаться в этом состоянии. В Win7 и выше вроде бы есть дрова для CDC COM порта. Насчёт USB сетевухи не уверен. Остаётся только вопрос можно ли с неадминской учеткой создавать сетевое подключение.
DaemonGloom
15.06.2017 07:19+1Дрова для некоторых USB сетевых карт есть изначально в windows 7 и более новых. Если нужно модемное подключение, то с неадминской учётки можно создать новое подключение, но нельзя запомнить пароль для всех пользователей — только для своего. Чего вполне хватает для получения интернета на устройстве. Чаще всего, новое подключение не нужно вообще, хватит стандартного сетевого подключения, создаваемого windows для каждой сетевой карты.
semax95
15.06.2017 07:56Многие модемы умеют прикидываться сетевой картой и прав админа для них не нужно, в системе уже все есть, сам в универе так делал.
Если у вас телефон на android подключите его как usb модем, думаю вы удивитесь.
Технология называется ndisSaymon
15.06.2017 11:35-1Отличное замечание! Да действительно, для подключение смартфона ( я проверял Android)
в качестве модема на win10 никаких драйверов и ПО не требуется.Но это меня натолкнуло на мысль, что мы слишком зациклились на модемах и подключению по usb. Действительно пользователь может подрубить к компу и usb-wifi( современные свистки большей частью тоже без установки драйверов), а смартфон превратить в точку доступа одним нажатием, и воткнуть сетевую карту wi-fi в pci слот на материнке, и принести роутер, который может использвать в качестве wan подключения wi-fi со смартфона либо также подключать usb-модем в роутер, а компьютер подключить по Ethernet. Соответственно в двух последних случаях метод защиты автора не будет действовать, но зато будет действовать банальное отключение Dhcp на брэндмауре.anatolymik
15.06.2017 11:38Можно фильтровать и PCI. Как вы себе представляете что пользователь рабочего ПК вставил плату? Нет можно конечно. Вы такое видели, среди не разработчиков?
Saymon
15.06.2017 11:44Я видел когда юзер чтобы поставить игру переустановил виндовс, т.к. ему не давали прав администратора для этого. А воткнуть плату для человека хоть раз собиравшего-разбиравшего комп не проблема и таких людей среди пользователей достаточно.
anatolymik
15.06.2017 11:48А как же ваши ранее упомянутые регламенты компании, расследования инцидентов и система наказаний? На которые, как я понял, надо ставить приоритет, а не на технические решения.
А воткнуть плату для человека хоть раз собиравшего-разбиравшего комп не проблема и таких людей среди пользователей достаточно.
Сколько я видел людей работающих за ПК, они кнопки с трудом нажимают. Что уж говорить о подключении сетевой платы.
И потом, PCI это все-таки не hot-plug. Когда USB массовый. Даже если и так, вы слишком много уделяете внимания вещам, которые по своей натуре маловероятны. Целесообразно ли тратить на защиту от таких случаев много сил?Saymon
15.06.2017 11:53А как же ваши ранее упомянутые регламенты компании, расследования инцидентов и система наказаний? На которые, как я понял, надо ставить приоритет, а не на технические решения.
Человека уволили в этот же день. Он как раз на испытательном сроке был.
Два правила на брендмауре это быстрее чем писать драйвер по всевозможные варианты usb и pci устройств.
mrkaban
14.06.2017 15:51+1Штука интересная, однако, мне кажется, что где-то видел бесплатное ПО для решения подобного вопроса, так сходу название вспомнить не могу.
Antelle
15.06.2017 00:24Вряд ли пользователь домашнего ПК заинтересуется тем, чтобы блокировать устройства на своем ПК
Полезные применения тоже есть, чтобы в комп не совали всякое.
gdt
16.06.2017 19:53Домашнего ПК да. А если у вас есть терминал МФУ — например, вы печатаете с USB, будет очень неприятно, если какой-нибудь умник подойдёт к вашему терминалу с USB клавиатурой.
Ivan_83
Более 1к строк шаблонной копипасты кода только чтобы в мастдае написать простенький драйвер который портит юзби.
А потом этот драйвер ещё нужно подписать специальным сертификатом за несколько сотен зелёных иначе венда его грузить не будет.
На других системах проблема решается правкой конфигов udev/devd.
anatolymik
Очень интересное утверждение.
На счет шаблонов, а где их нет?
Я правильно понимаю что конфиг udev/devd не портит USB? Т.е. если это драйвер, значит портит, а если конфиг, который читает драйвер, то не портит. Так?
Касательно конфигов, как уже было упомянуто, драйвер простой, его вполне можно доработать чтобы не требовалась перезагрузка. Более того, если идти дальше, можно много внести всяких гибкостей, например блокирование записи и т.д. У вас это получиться на других системах простой правкой конфига?
Kolyuchkin
Функционала UDEV хватит, чтобы писать гибкие правила проверки и блокировки устройств, в том числе и с привязкой к временным промежуткам)
anatolymik
Хватит. Но когда речь идет о гибкости которая не заложена, по-моему, подобный подход решит эту проблему. Я не утверждал о простой блокировки записи. Безусловно мы его смешиваем с анализом и принятием решения динамически. В прошлом посте я выразился не точно. Извиняюсь.
Ну и речь в статье, все-таки не об UDEV)))
Kolyuchkin
Дорогой автор, успокойтесь)) Ваша статья, как «вводный краткий курс» в системное программирование под ОС семейства MS Windows, имеет место быть — в свое время она бы мне помогла. Вот если бы Вы в статье уделили внимание еще и написанию inf-файлов, было бы совсем хорошо. А вот мониторить и конфигурировать драйвер можно через WMI-интерфейс.
anatolymik
Расскажите, как с USB решить дело. Я решений не искал. Было бы интересно узнать.
Kolyuchkin
Приведу просто ссылку — вот
anatolymik
WMI будет работать только когда драйвер его поддерживает. Драйвер шины USB поддерживает соответствующий функционал и предоставляет для него интерфейсы? Вопрос заключался в этом.
Kolyuchkin
Он будет поддерживаться, если вы в драйвере зарегистрируете соответствующего агента)) В DDK все описано и есть примеры.
anatolymik
Еще раз, драйвер USB в Windows, его поддерживает? И если да, то какие интерфейсы управления он предоставляет?
Kolyuchkin
Еще раз отвечаю, зачем Вам знать, поддерживает ли этот интерфейс существующий драйвер USB? Вы в своем фильтр-драйвере его поддержите и настраиваете с помощью него свои «белые (черные)» списки)
anatolymik
Драйвер USB шины, должен зарегистрироваться с WMI как провайдер. Иначе он не будет управляться через WMI.
А тогда фильтр зачем?
Kolyuchkin
А Вы разве не фильтр-драйвер написали? В своей статье Вы так его и обозвали — фильтр верхнего уровня.
anatolymik
А это вопрос к вам был? Зачем фильтр, если через WMI управлять можно? Вы утверждали что в фильтре надо это сделать.
Kolyuchkin
Фух, устал я)) Ваш фильтр-драйвер Вам нужен для реализации Вашей логики блокировки подключения «ненужных» USB-устройств. А вот конфигурировать список «ненужных» USB-устройств предлагаю реализовать через WMI. Вот и все. Сами же писали, что в этом у вас недоработка, что при изменении списка «ненужных» USB-устройств требуется перезагрузить компьютер.
anatolymik
Недоработка и недостаток не одно и тоже.
А прикрутив я WMI к своему фильтру, чтобы я выиграл стратегически? Кроме потерянного времени?
Kolyuchkin
Вот за то время, которое Вы потратили на пререкания со мной, Вы могли бы уже прикрутить к своему драйверу провайдер WMI, и время не было бы потрачено зря — Вам новые знания, Вашим читателям польза. И, по моему сугубо-субъективному мнению, время, потраченное на приобретение новых знаний и применение этих знаний на практике, никогда не станет потерянным. Я не вижу смысла продолжать эту ветвь комментариев — засим откланиваюсь.
anatolymik
Ваша позиция понятна. «В интернете кто-то не прав» (С).
anatolymik
Тоже ссылку приведу: вот
Ivan_83
Я не против шаблона, я против того что он такой охренительно большой.
У вас же там сплошные затычки, что мешало индусам реализовать в ядре простую логику уровня:
if (NULL == cb_proc) {
и вот тут код возврата из затычки.
}
?
(А вообще я уже забыл, может оно там так и сделано, вроде обязательным был только AddDevice и он же был как то отдельно от прочих хэндлеров, и тогда претензия не к индусам а к вам).
Потом, что во фре что в линухе архитектура дров более удачная, там вот этого всего непотребства нет, дрова там много проще, а функционал не уступает.
Для того чтобы вот так сломать работу с накопителями достаточно накорябать немного в конфиг, а не писать свой ядерный драйвер.
Чтобы понять что происходит на других системах нужно понять что там всё по другому.
Например, обычно юзер не может монтировать диски.
У меня диски монтируются простым скриптом, который вызывается из devd при обнаружении дисковых устройств.
Разумеется, поправив этот скрипт или просто скопировавав и поменяв rw на ro и прописав в devd вызов копии для списка устройств можно добиться что диск будет монтироваться только для чтения.
Но если хочется через жопу, те ядро, то на фре можно накорябать geom ноду, которая будет на 99% состоять из шаблона-затычек, и только для записи там будет ретурн с ошибкой, ну и вешать её через тот же devd.
А ещё никто не запрещает взять и наколбасить прямо в коде юзби подсистемы всё что хочется.
Плюс ты тут ничего не написал про отладку всего это счастья.
Даже как хотя бы просто там понапихать везде printf() и потом ловить сообщения. В других ОС такой проблемы нет при отладке, даже более того, есть сразу встроенные отладчики, которые можно сделать чтобы вызывались при краше ОС/драйвера. Ну и корки пишутся и там вполне подробно может быть что и где упало, вплоть до номера строчки и содержимого этой строчки.
А что до общения с драйвером, обычно делают какое то устройство, из проги его открывают через CreateFile() и херачат туда ioctl(). Потом правда оказывается что кто то таким образом позволяет любым приложениям перезаписывать/читать любой участок ядерной памяти, но это отдельная тема :)
2 VBKesha
Проверку отключить можно, но появится достающая надпись везде что мастдай в режиме разработчика/отладки, ты серьёзно предлагаешь так жить после отладки?
Единственный рабочий бесплатный вариант, который может быть всё ещё доступен это опенсорсить драйвер и просить ребят из реактос чтобы подписали.
2 redmanmale
2 Saymon
http://netlab.dhis.org/wiki/ru:hardware:huawei:e3272
HiLink и NCM режимы работы.
Но модем должен быть предварительно настроен чтобы он сразу был виден при подключении, а то по дефолту там часто режим когда виден сидюк, с которого вкорячивается софт, который просто посылает или eject сидюку или специальный код, это часто гасит сидюк и активирует всё остальное.
anatolymik
Вы вообще о чем?