Может быть, ввести нормальный протокол аутентификации через цифровую подпись?
Например, в каждый блок пользовательских данных, передаваемых через DeviceIoControl, ввести два поля: номер запроса и поле цифровой подписи?
Идея заключается в следующем. Пусть в незащищённом режиме пользовательские данные передаются структурой struct UserData. Тогда защиту можно ввести так:
struct AuthenticatedData {
unsigned long messageLength;
unsigned long sessionId; // идентификатор сессии, создаётся при предварительном установлении соединения
unsigned long sequenceNumber; // номер сообщения в последовательности
struct UserData data;
struct Signature signature;
};
Поле messageLength задаёт полную длину данных вместе с цифровой подписью и служебными полями. Поле sequenceNumber необходимо для того, чтобы защититься от атаки "воспроизведение перехваченных сообщений" (replay attack), в которой противник перехватывает сообщение и затем посылает его, чтобы заставить драйвер выполнить команду, которая как-будто исходит от правильного приложения. Пример использования такой атаки см. на этом форуме в топике про отключение файервола
https://forum.shelek.ru/index.php/topic,7737.0 . После каждого вызова DeviceIoControl приложение увеличивает номер, а драйвер отбрасывает все сообщения, номер которых не больше номера последнего полученного сообщения. То есть каждое сообщение обрабатывается не более одного раза, и перехват команд не поможет противнику.
Цифровая подпись необходима для того, чтобы обеспечить целостность сообщения -- если противник изменит хотя бы один бит в данных или номере последовательнсти, то цифровая подпись не сойдётся. В частности, это поможет защититься от увеличения номера в перехваченном сообщении. Сообщения с неверной подписью драйвер должен отбрасывать (не обрабатывать) и, возможно, помещать сообщение в лог о попытке подделать сообщение.
Ответы драйвера необходимо защищать аналогичным образом, чтобы быть уверенным, что противник не подделывает данные от драйвера.
Для того, чтобы противник не смог подделать цифровую подпись, в её состав должен входить секретный ключ, который известен только драйверу и приложению. Даже если противник знает алгоритм, по которому формируется подпись, он не сможет вычислить ключ по перехваченным сообщениям и правильно подписывать поддельные сообщения.
Для цифровой подписи есть много надёжных алгоритмов с известной и проверенной реализацией, например HMAC-MD5, HMAC-SHA1, HMAC-SHA2.
Для реализации предложенной схемы обмен данными между приложением и драйвером необходимо предварить операцией по генерации сессионного ключа, то есть ключ не хранится в коде приложения/драйвера, а генерируется каждый раз при запуске приложения. Для решения этой задачи как правило используются подходы, основанные на схеме Диффи-Хеллмана. Есть открытые надёжные реализации различных вариантов схемы Диффи-Хеллмана.
На фазе генерации ключа приложение предъявляет аутентифицирующие данные, например сертификат. Предполагается, что сертификатом может пользоваться только приложение, которому сертификат выдали разработчики драйвера. Если сертификат неверен, то драйвер отказывается устнавливать сеанс с приложением, и приложение не знает, какой sessionId и с каким ключём использовать, то есть не может формировать правильные сообщения.
Если драйвер уже реализован, и нет возможности добавить функции защиты непосредственно в него, то всю криптографическую составляющую можно вынести в фильтр-драйвер, который будет устанавливать сеанс, и валидировать данные. Если данные аутентифицируются верно, то фильтр-драйвер извлекает из сообщения UserData и передаёт их в защищаемый драйвер. Ответ от драйвера фильтр-драйвер снабжает sessionId и подписывает.
Вот такая минимальная схема аутентификации.