создание сырого сокета (дескриптора) и перевод его в неразборчивый режим
Совсем иная ситуация складывается с Windows NT. Пакетных сокетов она не поддерживает, с сетевым драйвером напрямую работать не позволяет. Точнее позволяет, но с очень большими предосторожностями и не без плясок с бубном (если нет бубна, на худой конец сойдет и обыкновенный оцинкованный таз, подробное изложение ритуала можно найти у Коберниченко в "Недокументированных возможностях Windows NT"). И хотя пакетных снифферов под NT существует огромное количество (один из которых даже входит в DDK), все они требуют обязательной установки специального драйвера, т. к. корректная работа с транспортом в NT возможна только на уровне ядра. Может ли червь притащить с собой такой драйвер и динамически загрузить его в систему? Ну, вообще-то может, только это будет крайне громоздкое и неэлегантное решение.
В Windows 2000/XP все гораздо проще. Там достаточно создать сырой сокет (в Windows 2000/XP наконец-то появилась поддержка сырых сокетов!), повестить его на прослушиваемый интерфейс и, сделав сокету bind, перевести последний в неразборчивый режим, сказав WSAIoctl(raw_socket, SIO_RCVALL, &optval, sizeof(optval), 0,0,&N,0,0)), где optval – переменная типа DWORD с единицей внутри, а N – количество возращенных функцией байт.
Впервые исходный текст такого сниффера был опубликован в шестом номере журнала #29A, затем его передрал Z0mnie, переложивший ассемблерный код на интернациональный программистский язык Си++ (странно, а почему не Си?) и унаследовавший все ляпы оригинала. Ниже приведен его ключевой фрагмент с моими комментариями, а полный исходный текст содержится в файле sniffer.с. Другой источник вдохновления – демонстрационный пример IPHDRINC, входящий в состав Platform SDK 2000. Рекомендую.
// создаем сырой сокет
//------------------------------------------------------------------------
if ((raw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IP)) == -1) return -1;
// вот тут некоторые руководства утверждают, что сырому сокету надо дать
// атрибут IP_HDRINCL. дать-то, конечно, можно, но ведь можно и не давать!
// флаг IP_HDRINCL сообщает системе, что аплеуха хочет сама формировать
// IP заголовок отправляемых пакетов, а принятые пакеты ей отдаются с IP
// заголовком в любом случае. подробности в PlatformSDK->
TCP/IP Raw Sockets
//if (setsockopt(raw_socket, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(optval))== -1)…
// перечисляем все интерфейсы (т.е. адреса IP-адресов всех шлюзов, что
// есть на компьютере. при ppp-подлюкчении к инетернет обычно имеется
// всего один IP-адрес, назначенный DHCP сервером провайдера, однако,
// в локальной сети это не так
if ((zzz = WSAIoctl(raw_socket, SIO_ADDRESS_LIST_QUERY, 0, 0, addrlist,
sizeof(addrlist), &N, 0, 0)) == SOCKET_ERROR) return -1;
…
// теперь мы должны сделать bind на все интерфейсы, выделив каждый в свой
// поток (весь сокеты - блокируемые), однако, в данном демонстрационном
// примере слушается лишь IP первого попавшегося под руку интерфейса
addr.sin_family = AF_INET;
addr.sin_addr = ((struct sockaddr_in*) llist->
Address[0].lpSockaddr)->
sin_addr;
if (bind(raw_socket, (struct sockaddr*) &addr, sizeof(addr))==SOCKET_ERROR) return -1;
#define SIO_RCVALL 0x98000001
// сообщаем системе, что мы хотим получать все пакеты, проходящие мимо нее
if (zzz=WSAIoctl(raw_socket,SIO_RCVALL,&optval,sizeof(optval),0,0,&N,0,0)) return –1;
// получаем все пакеты, приходящие на данный интерфейс
while(1)
{
if ((len = recv(raw_socket, buf, sizeof(buf), 0)) < 1) return -1;
…
}