Пример реализации драйвера шифрования сетевых пакетов
С расшифрованием дела обстоят гораздо сложнее, так как в процессе получения пакета из сети в промежуточном драйвере участвуют сразу несколько функций: ProtocolReceive, ProtocolTransferDataComplete, ProtocolReceivePacket, MiniportRetumPacket, MiniportTransferData.
Так как в функцию ProtocolReceive может попасть не весь пакет, а только его начало, то расшифрование в этой функции осуществляется только в том случае, если в нее был передан весь пакет. Если в эту функцию попало только начало пакета, то будет вызвана функция NdisTransferData, которая заставит драйвер сетевой карты передать оставшуюся часть пакета, и тогда его расшифрование будет осуществляться в завершающей функции для функции NdisTransferData – функции ProtocolTransferDataComplete, которая уже будет обладать полным пакетом.
ProtocolReceivePacket необязательная функция, нужная только в том случае, если есть уверенность, что промежуточный драйвер будет располагаться над драйвером сетевой карты, имеющим способность предоставлять сразу несколько пакетов, полученных из сети. Если такой уверенности нет, то эту функцию можно не регистрировать, то есть при инициализации обнулить соответствующий указатель. Если эта функция все же реализуется, то она должна содержать процедуру расшифрования.
Необходимость расшифрования пакетов (в случае зашифрования первоначального пакета без его перекопирования), возвращаемых драйверу транспорта, в функции ProtocolSendComplete объясняется следующим образом. При отправлении данных из прикладной программы в сеть, переданные ею данные, проходя по стеку сетевых драйверов, не перекопируются в новые участки памяти, просто к ним добавляются разные сетевые заголовки, поэтому при зашифровании данных пакета в функции MiniportSendPackets или MiniportSend (если не создавался новый пакет и первоначальный не перекопировался в него) шифруются на самом деле данные в буфере, принадлежащем приложению, а оно вовсе может и не рассчитывать на это.
Может даже произойти следующее: если ключ шифрования не изменять, то при повторной посылке данных в сеть, эти данные, наоборот, расшифруются и пойдут по сети в открытом виде. Чтобы избежать этой неприятности, необходимо, перед возвращением отправленного пакета драйверу транспорта со статусом операции отправления, расшифровать его.
Функция MiniportTransferData предназначена для того, чтобы передавать драйверу транспорта оставшуюся часть полученного от драйвера сетевой карты пакета, ранее не переданную в драйвер транспорта с помощью функции NdisMXxxIndicateReceive. Но если промежуточный драйвер всегда передает полученные пакеты наверх с помощью вызова функции NdisMIndicateReceivePacket, то ему не надо обеспечивать функцию MiniportTransferData.
При разработке драйвера шифрования важно помнить, что цепь NDIS-буферов в NDIS-пакете не всегда точно соответствует сетевым заголовкам (Ethernet, IP, TCP/UDP). NDIS-пакеты, передаваемые из драйвера протокола, например TCP/IP, в промежуточный драйвер для отправки, могут иметь очень разнообразную структуру, то есть разное количество NDIS-буферов и их содержимое. Вот лишь некоторые примеры (смотри рис. 31), которые удалось пронаблюдать с помощью программы-отладчика Soft Ice. (Разделение означает различные NDIS-буфера, заметьте, что Ethernet заголовок уже пристроен к пакету драйвером протокола TCP/IP.)
При разборе пакета перед зашифрованием (чтобы определить, например, следует ли его шифровать) нужно учитывать, что данные пользователя и сетевые заголовки могут находиться в разных участках памяти, а иногда и вместе (в пакетах протокола ARP_RARP).
При разработке драйвера шифрования полезно использовать анализатор протоколов, например, Network Monitor, наблюдая с его помощью содержимое перехваченных пакетов. Если его драйвер-перехватчик, под названием bh.sys, привязать к реальной сетевой карте, то перехваченные им пакеты будут зашифрованными. Если же его привязать к виртуальной сетевой карте, создаваемой промежуточным драйвером шифрования, то перехваченные им пакеты будут уже расшифрованными.
Рис. 31. Структуры NDIS-пакетов