Хакер № 03/04 (63)

Препарируем IP
Константин Клягин
Xakep, номер #063, стр. 063-116-2
По адресу www.insecure.org/sploits/ping-o-death.html находится описание POD вместе с примером его использования. Чтобы не изобретать велосипед, обратимся к приведенному там исходнику, который был написан еще в далеком 96.
Для отсылки произвольного пакета нужно выделить под него кусок памяти, а затем, правильно заполнив заголовок и добавив данные, отослать. Опыт показывает, что делать это все руками довольно сложно. Риск составить неправильный заголовок, из-за которого пакет будет зарублен локально, очень велик. Поэтому для определения заголовка пакета лучше использовать специальные структуры. struct ip дает доступ ко всем нужным полям, поэтому, "натянув" ее на буфер, можно не беспокоиться за правильность формата. Точно так же можно работать и со структурой пакета ICMP, для которого имеется свой тип struct icmphdr:
char buf[1500];
struct ip *ip = (struct ip *)buf;
struct icmphdr *icmp = (struct icmphdr *)(ip + 1);
В самом начале, где открывается сокет, мы видим заветную константу - SOCK_RAW. Она говорит о том, что мы работаем с raw sockets, то есть с "сырыми" сокетами. Слово "сырые" здесь означает степень готовности, а не относительную влажность :). То есть мы сами будем конструировать пакеты протокола. Это, в свою очередь, означает, что если при отсылке UDP или установлении соединений по TCP система обычно сама занимается проставлением нужных полей - даты, адреса отправки и назначения, контрольной суммы и прочего, то в случае с raw забота обо всем этом перекладывается на крепкие плечи программиста:
...
if ((s = socket(AF_INET, SOCK_RAW,
IPPROTO_ICMP
)) < 0) {
perror("socket");
Кстати, в UNIX создание таких сокетов дозволяется исключительно пользователю root. Поэтому работать придется от него. Ну, или компилировать от себя, а запускать через sudo, кому как нравится.
...
ip->ip_v = 4;
ip->ip_hl = sizeof *ip >> 2;
ip->ip_tos = 0;
ip->ip_len = FIX(sizeof buf);
ip->ip_id = htons(4321);
ip->ip_off = FIX(0);
ip->ip_ttl = 255;
ip->ip_p = 1;
dst.sin_addr = ip->ip_dst;
dst.sin_family = AF_INET;
Отсылка производится одним вызовом sendto(). В приведенном выше исходнике цикл применяется исключительно для того, чтобы послать большой пакет по фрагментам. Есть у IP и такая возможность. Дело в том, что 65535 байт - ограничение не только для принимающей стороны. При попытке послать пакет большего размера любая система ответит отказом, а perror() скажет: "Message too long".
Вызов sendto()
for (offset = 0; offset < 65536; offset += (sizeof buf - sizeof *ip)) {
Назад на стр. 063-116-1 Содержание Вперед на стр. 063-116-3
|