ПЗ (1195114), страница 2
Текст из файла (страница 2)
Информация предаётся через входящий буфер. Для проверки необходимо воспользоватся Функцией IOCTL MY_ICTRL_ReadTablePravilo.
Также эта функция вызывает фукцию WriteSettings для того, чтобы сохранить произведённые изменения в настройках в файл.
Функция IOCTL MY_ICTRL_ReadTablePravilo используется для чтения правил из драйвера. Она передаёт сразу всю таблицу правил. Количество элементов определяется путём деления размера переданной информации на размер одного правила, если деление произошло с остатком, то это действие ошибочное.
Функция IOCTL MY_ICTRL_WriteTableHTTPHost используется для передачи списка запрещённых имён хостов. Список представляет собой набор строк заканчивающихся нулём. Сам список заканчивается пустой строкой. Также необходимо передать полный размер списка.
Также эта функция вызывает фукцию WriteSettings для того, чтобы сохранить произведённые изменения в настройках в файл.
Функция IOCTL MY_ICTRL_WriteTableHTTPURL используется для передачи списка запрещённых имён файлов. Список представляет собой набор строк заканчивающихся нулём. Сам список заканчивается пустой строкой. Также необходимо передать полный размер списка.
Также эта функция вызывает фукцию WriteSettings для того, чтобы сохранить произведённые изменения в настройках в файл.
Функция IOCTL MY_ICTRL_GetQueryPacket реализует механизм передачи пакетов на уровень пользователя. Для этого необходимо создать правило, в котором действие обозначается как «Запрос». Все пакеты, подпадающие под это правило помещаются в специальную очередь и получат идентификатор.
После вызова этой функции пакет помещается в очередь ожидающих пакетов, где к нему можно обратится используя одну из следующих функций: MY_ICTRL_GetQP_PacketDataSize, MY_ICTRL_GetQP_PacketData и MY_ICTRL_QP_PacketDel.
Данная функция передаёт на уровень пользователя Идентификатор, время поступления пакета и направление пакета.
Функция IOCTL MY_ICTRL_GetQP_PacketDataSize передаёт размер всего пакета для определения, какого именно пакета в функцию передаётся идентификатор пакета. Данную функцию необходимо вызывать перед получением пакета с помощю функции MY_ICTRL_GetQP_PacketData для определения размера пакета.
Функция IOCTL MY_ICTRL_GetQP_PacketData передаёт содержимое пакета на уровень пользователя для определения, какого именно пакета в функцию передаётся идентификатор пакета. Пакет передаётся полностью вместе Ethernet заголовком, IP заголовком, TCP/UDP заголовком и содержимым пакета.
Функция IOCTL MY_ICTRL_QP_PacketDel используется для удаления пакета из очереди ожидания.
3.4 Фильтр пакетов
Одной из основных функций моего драйвера является фильтрация пакетов. Проходящих через него пакетов. Так как мой драйвер является Минипортом промежуточного звена NDIS. Через него проходят все пакеты, направляемые на сетевые адаптеры, модемы и виртуальные соединения(VPN), а также все получаемые пакеты.
Для передачи пакетов в NDIS используются специальные пакеты. При получении данных из сети драйвер сетевой карты выделяет ресурсы под NDIS-пакет и помещает в него полученные данные. Также работает и драйвер протокола.
NDIS-пакет является структурой NDIS_PACKET которая содержит следующие поля(Рисунок 3.4):
-
поле PhysicalCount содержит количество физических страниц в пакете;
-
поле TotalLength содержит длину данных в пакете;
-
поля Head, Tail ууказывают на первый и последний буферы пакета;
-
поле Flags – Флаги;
-
закрытые данные для драйвера и минипорта.
Буферы являются структурой NDIS_BUFFER, которая в свою очередь является переименованной структурой MDL. Структура MDL имеет следующие поля(Рисунок 3.4):
-
поле Next указатель на следующий буфер;
-
поля Size содержит размер буфера;
-
поля MdlFlags содержит флаги;
-
поле MappedSystemVa указывает начальный виртуальный адрес страници;
-
поле StartVa указывает начальный виртуальный адрес буфера;
-
поле ByteCount содержит количество байт в буфере;
-
поле ByteOffset содержит смещение от начала страници.
Рисунок 3.4 – Структура и взаимодействие NDIS пакета, NDIS буфера, и содержащегося в NDIS пакете Ethernet пакета.
В NDIS-пакете данные идут не подряд, а разбиваются кусочки. За каждый такой кусочек отвечает свой буфер. Обычно разные заголовки вложенных пакетов идут в отдельных буферах это связано с тем что данные заголовки заполняются разными драйверами, так же весь пакет может быть в одном буфере, обычно такую структуру имеет пакет идущий от драйвера сетевой карты к драйверу протокола(Рисунок 3.5)/4/.
В связи с такой сложной организацией пакета для доступа к данным пакета пришлось разрабатывать специальные функции(Листинг 3.1):
-
функция GetBufferSize определяет общий размер цепочки буферов;
-
функция GetPacketDataSize определяет размер данных в пакете;
-
функция CopyPacketData копирует весь Ethernet пакет в буфер.
Рисунок 3.5 – Заполнение буферов в NDIS-пакете.
Листинг 3.1 - Функции для работы с данными в пакете.
INT GetBufferSize(PNDIS_BUFFER Head,PNDIS_BUFFER Tail)
{
PNDIS_BUFFER Buff;
UINT S=0;
Buff = Head;
do
{
S+=Buff->ByteCount;
if (Buff==Tail) break;
Buff=Buff->Next;
}while(Buff!=NULL);
return S;
}
INT GetPacketDataSize(PNDIS_PACKET Packet)
{
return GetBufferSize(Packet->Private.Head,Packet->Private.Tail);
}
void CopyPacketData(PNDIS_PACKET Packet,char *Buffer)
{
PNDIS_BUFFER Buff;
UINT S=0;
Buff = Packet->Private.Head;
do
{
NdisMoveMemory(Buffer+S,((char*)((Buff->StartVa)))+Buff->ByteOffset,Buff->ByteCount);
S+=Buff->ByteCount;
if (Buff==Packet->Private.Tail) break;
Buff=Buff->Next;
}while(Buff!=NULL);
}
Для фильтрации в точках входа MiniportSendPackets и ProtocolReceive вызывается функция фильтрации FilterIP (Листинг 3.2) которая разрешает или запрещает пакет в соответствии с действующими правилами.
Листинг 3.2 - Функция фильтрации FilterIP.
FW_Actions FilterIP(PNDIS_PACKET Packet,FW_Directions Directions)
{
int SizePacket = GetPacketDataSize(Packet);
char *DataPacket = NULL;
TPacketInfo PI;
UINT Allow = 1;
UINT PraviloID;
FW_Actions Actions = FWA_Allow;
// AddQueryPacket(Directions,Packet);
NdisAllocateMemory(&DataPacket,SizePacket,0,HAA);
CopyPacketData(Packet,DataPacket);
if (GetPacketTCPIPInfoEX(DataPacket,SizePacket,&PI)!=0)
{
FD1.NumIPPacket++;
PraviloID = GetPraviloID(&PI,Directions);
if (PraviloID!=0xFFFFFFFF)
{
if (PraviloIPTable[PraviloID].Flag&PIP_Allow)
Allow = FWA_Allow;
else
Allow = FWA_Block;
if (PraviloIPTable[PraviloID].Flag&PIP_HTTPFilter)
Allow = FWA_Block;
}
Actions=(Allow)?(FWA_Allow):(FWA_Block);
AddFirewallLog(&PI,Actions,Directions,PraviloID);
if (PraviloID!=0xFFFFFFFF)
if (PraviloIPTable[PraviloID].Flag&PIP_Query)
AddQueryPacket(Directions,Packet);
}
NdisFreeMemory(DataPacket,SizePacket,0);
return Actions;
}
Функция FilterIP копирует содержимое в отдельный буфер, это необходимо из за сложной организации данных в пакете. После этого он вызывает функцию GetPacketTCPIPInfoEX(Листинг 3.3), которая расшифровывает данные в пакете для TCPIP протокола, если пакет не является TCPIP пакетом то функция GetPacketTCPIPInfoEX возвращает false и в результате пакет разрешается для передачи, после чего при помощи функции GetPraviloID определяется правило под которое под подает пакет. Если пакет не подпадает, под какое либо правило, то пакет разрешается для передачи. Также ведётся лог всех обработанных пакетов, для этого вызывается функция AddFirewallLog.
Листинг 3.3 – Функция расшишифровки пакетов GetPacketTCPIPInfoEX
bool GetPacketTCPIPInfoEX(char*Data,int Size,PPacketInfo PI)
{
PIPHeader hIP;
int SizeIPPack;
PI->EthernetHead = (PEthernetHeader)(Data);
PI->EthernetHeadSize = sizeof(EthernetHeader);
FD1.NumIPPacketTest+=1;
PI->Data = NULL;
PI->DataSize = 0;
//#define ETHERTYPE_IP 0x0800 /* IP protocol */
if (PI->EthernetHead->type != 0x0008) return false;
FD1.NumIPPacketTest+=1000;
hIP =(PIPHeader)(Data+PI->EthernetHeadSize);
if (hIP==NULL) return 0;
if (((hIP->iph_verlen&0xF0)>>4)!=4) return false;//Выход если версия неравна 4
FD1.NumIPPacketTest+=1000000;
PI->IPHead = hIP;
PI->IPHeadSize = (hIP->iph_verlen&0x0F)*4;//Вычисление размера IP заголовка
PI->TCPHead = NULL;
PI->TCPHeadSize = 0;
PI->UDPHead = NULL;
PI->UDPHeadSize = 0;
SizeIPPack = Convert(PI->IPHead->iph_length);
if (PI->IPHead->iph_protocol==IPPROTO_TCP)
{
int NUM;
PI->TCPHead = (PTCPHeader)(Data+PI->EthernetHeadSize+PI->IPHeadSize);
if (PI->TCPHead==NULL) return false;
NUM=((PI->TCPHead->tcp_flags)>>4)&0x000F;
PI->Data = Data+PI->EthernetHeadSize+PI->IPHeadSize+4*NUM;
PI->DataSize =SizeIPPack-PI->IPHeadSize-4*NUM;
PI->TCPHeadSize = 4*NUM;//Размер TCP заголовка в байтах
}
else if (PI->IPHead->iph_protocol==IPPROTO_UDP)
{
PI->UDPHead = (PUDPHeader)(Data + PI->EthernetHeadSize +
PI->IPHeadSize);
if (PI->UDPHead==NULL) return false;
PI->UDPHeadSize =4*2;//Размер UDP заголовка в байтах
PI->Data = Data + PI->EthernetHeadSize + PI->IPHeadSize +
PI->UDPHeadSize;
PI->DataSize =SizeIPPack-PI->IPHeadSize-PI->UDPHeadSize;
}
return true;
}
В функции GetPacketTCPIPInfoEX в начале расшифровывается Ethernet заголовок(Рисунок 3.6, Листинг 3.4). Ethernet заголовок на аппаратном и программном уровне имеет разную структуру из-за того, что часть полей таких рак Преамбула, начальный разделитель и контрольная сумма генерируются чипом сетевой карты.
Рисунок 3.6 - Ethernet заголовок на програмном и апоратном уроне.
Листинг 3.3 - Описание Ethernet заголовка.
typedef struct EthernetHeader{
UCHAR dhost[6];
UCHAR shost[6];
USHORT type;
} EthernetHeader,*PEthernetHeader;
Для определения содержимого пакета используется поле тип пакета. Для протокола TCP/IP тип равен 0x0800. IP заголовок(Рисунок 3.7, Листинг 3.4) начинается сразу после поля тип в Ethernet заголовке, то есть со смещение 14 от начала пакета.
Рисунок 3.7 - IP заголовок.
Листинг 3.4 - Описание IP заголовка.
typedef struct IPHeader {
UCHAR iph_verlen; // Version and length
UCHAR iph_tos; // Type of service
USHORT iph_length; // Total datagram length
USHORT iph_id; // Identification
USHORT iph_offset; // Flags, fragment offset
UCHAR iph_ttl; // Time to live
UCHAR iph_protocol; // Protocol
USHORT iph_xsum; // Header checksum
ULONG iph_src; // Source address
ULONG iph_dest; // Destination address
} IPHeader,*PIPHeader;
В IP заголовке для проверки в начале, определяю версию протокола, она должна быть равна 4. После этого из поля iph_verlen получает размер заголовка в 32-х битовых словах. Если пакет является TCP пакетом, то определяю TCP заголовок и данные в нём. Аналогично поступаю, если пакет является UDP пакетом. Вся информация об анализе пакета помещается в структуру TPacketTCPIPInfo(Листинг 3.5).
Листинг 3.5 - Описание структуры TPacketTCPIPInfo.
typedef struct TPacketTCPIPInfo {
PEthernetHeader EthernetHead;
UINT EthernetHeadSize;
PIPHeader IPHead;
UINT IPHeadSize;
PTCPHeader TCPHead;















