hex.pp.ua

Использование драйвера TCP

Использование драйвера TCP с помощью Native API




Из всех имён устройств, которые создаёт драйвер Tcp (tcpip.sys), мы рассмотрим «\Device\Ip» и «\Device\Tcp». Будем считать, что hIp — это открытый хэндл на устройство «\Device\Ip», а hTcp — на устройство «\Device\Tcp». Код открытия полностью аналогичен приведённому.

Получение физического адреса компьютера по его IP-адресу называется ARP-запросом. Примерный код такого запроса (для RemoteIp, адрес складывается в буфер Buffer не менее 6 байт) выглядит так:

 IO_STATUS_BLOCK iosb;
 ARP_SEND_REPLY arpIn = {RemoteIp, LocalIp};

 NTSTATUS ns = ZwDeviceIoControlFile (hIp, NULL, NULL, NULL, 
  &iosb, IOCTL_IP_SEND_ARP, &arpIn, sizeof arpIn, Buffer, BufferLen);

 if (ns == STATUS_PENDING)
 {
 //ожидание на hIp
 }

Простейший PING с объёмом дополнительно передаваемой информации равным нулю, выглядит так:

 DWORD PingTimeout; //таймаут самого пинга, задаётся в миллисекундах
 LONG RemoteAddress; //адрес удалённого компьютера

 IO_STATUS_BLOCK iosb;
 ICMP_ECHO_REPLY outdata = {0};
 ULONG dwInSize = sizeof(IP_PING_IN);
 if (sizeof(ICMP_ECHO_REPLY) > dwInSize) dwInSize = sizeof(ICMP_ECHO_REPLY);

 PIP_PING_IN pInBuffer = (PIP_PING_IN)malloc(dwInSize);
 pInBuffer->dest_address = RemoteAddress;
 pInBuffer->time_out = PingTimeout;
 pInBuffer->header_size = sizeof(IP_PING_IN);
 pInBuffer->req_data_start = pInBuffer->header_size;
 pInBuffer->ttl = 0xFF;
 //все остальные поля pInBuffer нулевые

 NTSTATUS ns = ZwDeviceIoControlFile (hIp, NULL, NULL, NULL, &iosb, 
  IOCTL_IP_PING, pInBuffer, dwInSize, &outdata, sizeof(outdata));

 if (ns == STATUS_PENDING)
 {
 //ожидание на hIp
 }

 //тут весь результат уже находится в outdata, в том числе:
 //outdata.Status — код ошибки самого пинга.
 //outdata.RoundTripTime — время ответа в миллисекундах.

Вывод списка соединений и прослушиваемых портов реализуется за 4 операции, 2 из них для TCP и 2 для UDP, в обоих случаях сначала получается размер списка (как поле в общей статистике протокола), а потом он и сам считывается. Для систем с номером версии 5.1 и выше возможен прямой «расширенный» запрос, при котором вернётся также идентификатор процесса. Приведённый код написан таким образом, что он учитывает этот факт введением переменной ExtendedRequest, которая должна быть равна FALSE для Windows 2000 и более ранних систем, и TRUE для Windows XP и выше. Кроме того, формат выходного буфера такого «расширенного» запроса немного отличается от формата выходного буфера «обычного»:

 IO_STATUS_BLOCK iosb;
 //эта переменая определяет,
 //будет ли запрос «расширенным»
 BOOLEAN ExtendedRequest = (((Peb->NtMajorVersion > 4) 
  && (Peb->NtMinorVersion > 0))) || (Peb->NtMajorVersion > 5);
 //получение размера таблицы для TCP
 TCP_REQUEST_QUERY_INFORMATION_EX InData;
 MIB_TCPSTATS TcpStats = {0};

 InData.ID.toi_entity.tei_entity = 0x400;
 InData.ID.toi_entity.tei_instance = 0;
 InData.ID.toi_class = 0x200;
 InData.ID.toi_type = 0x100;
 InData.ID.toi_id = 0x001;
 RtlZeroMemory(InData.Context,CONTEXT_SIZE);

 NTSTATUS ns = ZwDeviceIoControlFile (hTcp, NULL, NULL, NULL, &iosb, 
  IOCTL_TCP_QUERY_INFORMATION, &InData, 
  sizeof(TCP_REQUEST_QUERY_INFORMATION_EX), &TcpStats, 
  sizeof(MIB_TCPSTATS));

 if (ns == STATUS_PENDING)
 {
 //ожидание на hTcp
 }
 //здесь в буфер TcpStats записана текущая статистика TCP

 //размер буфера, который должен быть выделен для получения списка соедиений, равен:
 ULONG dwBufferSize = (TcpStats.dwNumConns * sizeof(MIB_TCPEXROW)) + 0xD4;
 PVOID pBuffer = malloc(dwBufferSize);
 InData.ID.toi_id = 0x101;
 if (ExtendedRequest) InData.ID.toi_id++;

 NTSTATUS ns = ZwDeviceIoControlFile (hTcp, NULL, NULL, NULL, &iosb, 
  IOCTL_TCP_QUERY_INFORMATION, &InData, sizeof(TCP_REQUEST_QUERY_INFORMATION_EX), 
  pBuffer, dwBufferSize);
 if (ns == STATUS_PENDING)
 {
 //ожидание на hTcp
 }
 //здесь в pBuffer записана информация по соединениям TCP.
 //если запрос «обычный», то pBuffer — массив из структур MIB_TCPROW, 
 // при расширенном запросе буфер заполняется структурами MIB_TCPEXROW.

 //а сейчас очередь получения статистики для UDP:
 MIB_UDPSTATS UdpStats = {0};
 //на самом деле несложно написать код,
 //который будет использовать один буфер и для TCP и для UDP.

 InData.ID.toi_entity.tei_entity = 0x401;
 InData.ID.toi_entity.tei_instance = 0;
 InData.ID.toi_class = 0x200;
 InData.ID.toi_type = 0x100;
 InData.ID.toi_id = 0x001;
 RtlZeroMemory(InData.Context,CONTEXT_SIZE);

 NTSTATUS ns = ZwDeviceIoControlFile (hTcp, NULL, NULL, NULL, &iosb, 
  IOCTL_TCP_QUERY_INFORMATION, &InData, sizeof(TCP_REQUEST_QUERY_INFORMATION_EX), 
  &UdpStats, sizeof(MIB_UDPSTATS));

 if (ns == STATUS_PENDING)
 {
 //ожидание на hTcp
 }
 //здесь в буфер UdpStats записана текущая статистика UDP

 //размер буфера, который должен быть выделен 
 //для получения списка открытых портов, равен:
 dwBufferSize = (UdpStats.dwNumAddrs * sizeof(MIB_UDPEXROW)) + 0xD4;
 free(pBuffer); pBuffer = malloc(dwBufferSize);
 InData.ID.toi_id = 0x101;
 if (ExtendedRequest) InData.ID.toi_id++;

 NTSTATUS ns = ZwDeviceIoControlFile (hTcp, NULL, NULL, NULL, &iosb, 
  IOCTL_TCP_QUERY_INFORMATION, &InData, sizeof(TCP_REQUEST_QUERY_INFORMATION_EX), 
  pBuffer, dwBufferSize);

 if (ns == STATUS_PENDING)
 {
 //ожидание на hTcp
 }
 //здесь в pBuffer записана информация по открытым портам UDP.
 //если запрос «обычный», то pBuffer — массив из структур MIB_UDPROW, 
 //при расширенном запросе буфер заполняется структурами MIB_UDPEXROW.

Вот так получается статистика по протоколам и таблица по соединениям TCP и открытым портам UDP соответственно. Во всем этом безумстве несложно заметить определённое сходство в заполнении параметров TCP_REQUEST_QUERY_INFORMATION_EX. Если это удалось, замечу, что статистику по IP (её тоже выводит netstat) можно получить, записав в InData.ID.toi_entity.tei_entity величину 0x301. Но это пусть останется в качестве домашней работы :).



Автор: Сергей Васкецов
Дата: 29.10.2002


При копировании материалов хорошим тоном будет указание авторства и ссылка на сайт. По поводу рекламы обращайтесь на почту [email protected]