Главная страница
Навигация по странице:

  • /usr/include/linux/types.h:typedef __u16 __bitwise __be16;

  • Добавлено в hacking-network.h define ETHER_ADDR_LEN 6define ETHER_HDR_LEN 14 260

  • Фрагмент /usr/include/netinet/ip.h

  • Добавлено в hacking-network.h

  • Фрагмент /usr/include/netinet/tcp.h

  • Хакинг. Хакинг__искусство_эксплоита_2_е_469663841. Книга дает полное представление о программировании, машин ной архитектуре, сетевых соединениях и хакерских приемах


    Скачать 2.5 Mb.
    НазваниеКнига дает полное представление о программировании, машин ной архитектуре, сетевых соединениях и хакерских приемах
    АнкорХакинг
    Дата16.06.2022
    Размер2.5 Mb.
    Формат файлаpdf
    Имя файлаХакинг__искусство_эксплоита_2_е_469663841.pdf
    ТипКнига
    #595131
    страница28 из 51
    1   ...   24   25   26   27   28   29   30   31   ...   51

    0x443 Декодирование уровней
    В наших перехваченных пакетах самым внешним уровнем, а также са- мым низким из видимых, является Ethernet. На этом уровне происхо- дит передача данных между устройствами Ethernet с помощью MAC- адресов. Заголовок этого уровня содержит MAC-адреса отправителя и получателя, а также 16-разрядное число, описывающее тип Ethernet- пакета. В Linux структура этого заголовка определена в /usr/include/
    linux/if_ethernet.h, а структуры заголовков IP и TCP находятся в /usr/
    include/netinet/ip.h и /usr/include/netinet/tcp.h соответственно. В исхо- дном коде tcpdump тоже есть структуры этих заголовков, и с таким же успехом мы могли создать их сами по RFC, где они описаны. Чтобы лучше разобраться в материале, полезно написать эти структуры само- стоятельно, поэтому, руководствуясь описаниями, создадим собствен- ные структуры заголовков, которые поместим в hacking-network.h.

    0x440 Анализ сетевых пакетов (сниффинг)
    259
    Сначала посмотрим, какое описание заголовка Ethernet у нас уже есть.
    Фрагмент /usr/include/if_ether.h
    #define ETH_ALEN 6 /* Байтов в Ethernet-адресе */
    #define ETH_HLEN 14 /* Всего байтов в заголовке */
    /*
    * Заголовок Ethernet-пакета.
    */
    struct ethhdr {
    unsigned char h_dest[ETH_ALEN]; /* Адрес получателя */
    unsigned char h_ source[ETH_ALEN]; /* Адрес отправителя */
    __be16 h_proto; /* Идентификатор типа пакета */
    } __attribute__((packed));
    В этой структуре есть три элемента заголовка Ethernet. Тип перемен- ной _
    _be16 оказывается 16-разрядным unsigned short integer. Это вы- ясняется, если рекурсивно выполнить grep определения типа во вклю- чаемых файлах.
    reader@hacking:

    /booksrc $
    $ grep -R “typedef.*__be16” /usr/include
    /usr/include/linux/types.h:typedef __u16 __bitwise __be16;
    $ grep -R “typedef.*__u16” /usr/include | grep short
    /usr/include/linux/i2o-dev.h:typedef unsigned short __u16;
    /usr/include/linux/cramfs_fs.h:typedef unsigned short __u16;
    /usr/include/asm/types.h:typedef unsigned short __u16;
    $
    Во включаемом файле также определен размер Ethernet-заголовка ETH_
    HLEN
    , равный 14 байт. Это совпадает с суммой размеров MAC-адресов от- правителя и получателя, каждый из которых имеет длину 6 байт, и поля типа пакета, имеющего тип 16-разрядного короткого целого, занима- ющего 2 байта. Однако многие компиляторы выравнивают структуры по 4-байтным границам путем дописки, вследствие чего sizeof(struct ethhdr)
    возвращает неверный размер. По этой причине размер Ethernet- заголовка следует брать из ETH_HLEN или как фиксированное значение
    14 байт.
    В результате включения будут также включены дру- гие файлы, содержащие необходимое определение типа _
    _be16. Так как мы хотим создать собственные структуры для hacking-network.h, ссыл- ки на неизвестные типы нужно удалить. Заодно дадим полям структу- ры более удачные имена.
    Добавлено в hacking-network.h
    #define ETHER_ADDR_LEN 6
    #define ETHER_HDR_LEN 14

    260
    0x400Сетевое взаимодействие struct ether_hdr {
    unsigned char ether_dest_addr[ETHER_ADDR_LEN]; // MAC-адрес получателя unsigned char ether_src_addr[ETHER_ADDR_LEN]; // MAC-адрес отправителя unsigned short ether_type; // Тип пакета Ethernet
    };
    То же самое проделаем для структур IP и TCP, основываясь на соответ- ствующих структурах и диаграммах в RFC.
    Фрагмент /usr/include/netinet/ip.h
    struct iphdr
    {
    #if __BYTE_ORDER == __LITTLE_ENDIAN
    unsigned int ihl:4;
    unsigned int version:4;
    #elif __BYTE_ORDER == __BIG_ENDIAN
    unsigned int version:4;
    unsigned int ihl:4;
    #else
    # error “Please fix
    #endif u_int8_t tos;
    u_int16_t tot_len;
    u_int16_t id;
    u_int16_t frag_off;
    u_int8_t ttl;
    u_int8_t protocol;
    u_int16_t check;
    u_int32_t saddr;
    u_int32_t daddr;
    /* Здесь начинаются параметры. */
    };
    Фрагмент RFC 791
    0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |Version| IHL |Type of Service| Total Length |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Identification |Flags| Fragment Offset |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Time to Live | Protocol | Header Checksum |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Source Address |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Destination Address |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Options | Padding |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    Пример заголовка интернет-дейтаграммы

    0x440 Анализ сетевых пакетов (сниффинг)
    261
    Каждый элемент структуры соответствует полям, показанным на ди- аграмме заголовка в RFC. Так как размер первых двух полей Version и IHL (от Internet Header Length – размер интернет-заголовка) всего в 4 бита, а в языке С нет 4-разрядных типов переменных, в определе- нии заголовка, взятом из Linux, байт делится по-разному – в зависи- мости от порядка байтов, принятого в архитектуре машины. Эти поля записаны в сетевом порядке байтов, поэтому в архитектуре «сначала младший байт» (little-endian) IHL расположен перед Version, посколь- ку порядок байтов инвертирован. В наших программах эти поля не ис- пользуются, поэтому даже делить байт пополам нам не нужно.
    Добавлено в hacking-network.h
    struct ip_hdr {
    unsigned char ip_version_and_header_length; // Версия и размер заголовка unsigned char ip_tos; // Тип сервиса unsigned short ip_len; // Общая длина unsigned short ip_id; // Идентификатор unsigned short ip_frag_offset; // Смещение сегмента и флаги unsigned char ip_ttl; // Максимальный срок существования unsigned char ip_type; // Протокол unsigned short ip_checksum; // Контрольная сумма unsigned int ip_src_addr; // IP-адрес отправителя unsigned int ip_dest_addr; // IP-адрес получателя
    };
    Как уже говорилось, компилятор дополнит структуру, чтобы выров- нять ее по 4-байтной границе. Размер IP-заголовка всегда 20 байт.
    Структуру TCP-заголовка мы возьмем из /usr/include/netinet/tcp.h, а его диаграмму – из RFC 793.
    Фрагмент /usr/include/netinet/tcp.h
    typedef u_int32_t tcp_seq;
    /*
    * TCP header.
    * Per RFC 793, September, 1981.
    */
    struct tcphdr
    {
    u_int16_t th_sport; /* Source Port */
    u_int16_t th_dport; /* Destination Port */
    tcp_seq th_seq; /* Sequence Number */
    tcp_seq th_ack; /* Acknowledgment Number */
    # if __BYTE_ORDER == __LITTLE_ENDIAN
    u_int8_t th_x2:4; /* (резерв) */
    u_int8_t th_off:4; /* Data Offset */
    # endif
    # if __BYTE_ORDER == __BIG_ENDIAN
    u_int8_t th_off:4; /* Data Offset */
    u_int8_t th_x2:4; /* (резерв) */

    262
    0x400Сетевое взаимодействие
    # endif u_int8_t th_flags;
    # define TH_FIN 0x01
    # define TH_SYN 0x02
    # define TH_RST 0x04
    # define TH_PUSH 0x08
    # define TH_ACK 0x10
    # define TH_URG 0x20
    u_int16_t th_win; /* Window */
    u_int16_t th_sum; /* Checksum */
    u_int16_t th_urp; /* Urgent Pointer */
    };
    Фрагмент RFC 793
    Формат TCP-заголовка
    0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Source Port | Destination Port |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Sequence Number |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Acknowledgment Number |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Data | |U|A|P|R|S|F| |
    | Offset| (резерв) |R|C|S|S|Y|I| Window |
    | | |G|K|H|T|N|N| |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Checksum | Urgent Pointer |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Options | Padding |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | data |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    Data Offset: 4 бита
    Количество 32-разрядных слов в TCP-заголовке. Указывает начало данных. Размер заголовка TCP (даже с опциями)
    всегда кратен 32 битам.
    Reserved: 6 бит
    Зарезервировано на будущее. Должны быть нули.
    Options: переменный размер
    1
    В структуре tcphdr из Linux также переключается порядок 4-разрядно- го поля смещения данных и 4-разрядного поля резерва в зависимости
    1
    Source Port – порт отправителя, Destination Port– порт получателя, Se- quence Number – порядковый номер, Acknowledgment Number – номер подтверждения, Data Offset – смещение данных, Window – размер окна,
    Checksum – контрольная сумма, Urgent Pointer – указатель срочности, Op- tions – параметры, Padding – дополнение пробелами. – Прим. перев.

    0x440 Анализ сетевых пакетов (сниффинг)
    263
    от архитектуры узла. Поле смещения данных (Data Offset) для нас су- щественно, потому что оно сообщает размер TCP-заголовка, который может меняться. Как вы могли заметить, в Linux в структуре tcphdr не оставлено места для опций TCP. Это связано с тем, что в RFC это поле определено как необязательное. Заголовок TCP всегда выравнивает- ся по 32 битам, и из смещения мы узнаем, сколько 32-разрядных слов в заголовке. Таким образом, размер TCP-заголовка в байтах равен зна- чению поля Data Offset, умноженному на 4. Так как поле смещения не- обходимо нам для вычисления размера заголовка, разобьем содержа- щий его байт, предположив, что байты в архитектуре узла располага- ются в порядке «сначала младший».
    Поле th_flags в структуре tcphdr определено как unsigned character
    (8 разрядов). Значения, определяемые ниже этого поля, представляют собой битовые маски, соответствующие шести возможным флагам.
    Добавлено в hacking-network.h
    struct tcp_hdr {
    unsigned short tcp_src_port; // TCP: порт отправителя unsigned short tcp_dest_port; // TCP: порт получателя unsigned int tcp_seq; // TCP: порядковый номер unsigned int tcp_ack; // TCP: номер подтверждения unsigned char reserved:4; // 4 из 6 бита резервного поля unsigned char tcp_offset:4; // TCP: смещение данных для узла
    “сначала младший”
    unsigned char tcp_flags; // TCP: флаги (и 2 бита из резерва)
    #define TCP_FIN 0x01
    #define TCP_SYN 0x02
    #define TCP_RST 0x04
    #define TCP_PUSH 0x08
    #define TCP_ACK 0x10
    #define TCP_URG 0x20
    unsigned short tcp_window; // TCP: размер окна unsigned short tcp_checksum; // TCP: контрольная сумма unsigned short tcp_urgent; // TCP: указатель срочности
    };
    Теперь, определив все заголовки в виде структур, мы можем написать программу, осуществляющую декодирование инкапсулированных за- головков каждого пакета. Но сначала задержимся немного на libpcap.
    В этой библиотеке есть функция pcap_loop(), которая выполняет пе- рехват пакетов лучше, чем циклический вызов pcap_next(). Функция pcap_next()
    очень редко используется в программах, поскольку ее при- менение некрасиво и неэффективно. Функция pcap_loop() использует функцию обратного вызова. Это значит, что функции pcap_loop() в ка- честве аргумента передается указатель на функцию, которая будет вы- зываться при каждом перехвате пакета. Прототип pcap_loop():
    int pcap_loop(pcap_t *handle, int count, pcap_handler callback, u_char *args);

    264
    0x400Сетевое взаимодействие
    Первый аргумент – дескриптор pcap, затем задаются количество паке- тов, которые нужно перехватить, и указатель на функцию обратного вызова. Если задать количество пакетов равным –1, цикл будет про- должаться бесконечно. Последний аргумент – необязательный указа- тель, который будет передан программе обратного вызова. Естествен- но, функции обратного вызова должен соответствовать некоторый прототип, поскольку pcap_loop() должна ее вызывать. Имя функции обратного вызова может быть любым, но аргументы должны быть та- кими:
    void callback(u_char *args, const struct pcap_pkthdr *cap_header, const u_char *packet);
    Первый аргумент – необязательный указатель, передаваемый pcap_
    loop()
    в качестве последнего аргумента. С его помощью можно пере- дать дополнительную информацию, но нам это не понадобится. Следу- ющие два аргумента знакомы нам по pcap_next() – это указатели на за- головок перехвата и на сам пакет.
    В следующем примере функция pcap_loop() и функция обратного вызо- ва осуществляют перехват пакетов и заполнение наших структур заго- ловков для их последующего декодирования. Объяснение работы про- граммы приведено после листинга.
    decode_sniff.c
    #include
    #include “hacking.h”
    #include “hacking-network.h”
    void pcap_fatal(const char *, const char *);
    void decode_ethernet(const u_char *);
    void decode_ip(const u_char *);
    u_int decode_tcp(const u_char *);
    void caught_packet(u_char *, const struct pcap_pkthdr *, const u_char *);
    int main() {
    struct pcap_pkthdr cap_header;
    const u_char *packet, *pkt_data;
    char errbuf[PCAP_ERRBUF_SIZE];
    char *device;
    pcap_t *pcap_handle;
    device = pcap_lookupdev(errbuf);
    if(device == NULL)
    pcap_fatal(“pcap_lookupdev”, errbuf);
    printf(“Sniffing on device %s\n”, device);

    0x440 Анализ сетевых пакетов (сниффинг)
    265
    pcap_handle = pcap_open_live(device, 4096, 1, 0, errbuf);
    if(pcap_handle == NULL)
    pcap_fatal(“pcap_open_live”, errbuf);
    pcap_loop(pcap_handle, 3, caught_packet, NULL);
    pcap_close(pcap_handle);
    }
    В начале программы объявлен прототип функции обратного вызо- ва с именем caught_packet(), а также несколько функций декодирова- ния. Все остальное в main() осталось практически прежним, только цикл for заменен одним вызовом pcap_loop(). Этой функции передают- ся pcap_handle, предписание перехватить 3 пакета и указатель на функ- цию обратного вызова caught_packet(). Последним аргументом являет- ся NULL, потому что дополнительных данных для передачи в caught_
    packet()
    нет. Заметим, что decode_tcp() возвращает u_int. Посколь- ку длина TCP-заголовка переменная, эта функция возвращает размер
    TCP-заголовка.
    void caught_packet(u_char *user_args, const struct pcap_pkthdr *cap_header, const u_char *packet) {
    int tcp_header_length, total_header_size, pkt_data_len;
    u_char *pkt_data;
    printf(“==== Got a %d byte packet ====\n”, cap_header->len);
    decode_ethernet(packet);
    decode_ip(packet+ETHER_HDR_LEN);
    tcp_header_length = decode_tcp(packet+ETHER_HDR_LEN+sizeof(struct ip_hdr));
    total_header_size = ETHER_HDR_LEN+sizeof(struct ip_hdr)+tcp_header_length;
    pkt_data = (u_char *)packet + total_header_size; // pkt_data указывает
    // на данные.
    pkt_data_len = cap_header->len - total_header_size;
    if(pkt_data_len > 0) {
    printf(“\t\t\t%u bytes of packet data\n”, pkt_data_len);
    dump(pkt_data, pkt_data_len);
    } else printf(“\t\t\tNo Packet Data\n”);
    }
    void pcap_fatal(const char *failed_in, const char *errbuf) {
    printf(“Fatal Error in %s: %s\n”, failed_in, errbuf);
    exit(1);
    }
    Функция caught_packet() вызывается, как только pcap_loop() перехва- тит пакет. Эта функция использует размеры заголовков, чтобы разде-

    266
    0x400Сетевое взаимодействие лить пакет по уровням, и функции декодирования, чтобы вывести де- тали заголовков каждого уровня.
    void decode_ethernet(const u_char *header_start) {
    int i;
    const struct ether_hdr *ethernet_header;
    ethernet_header = (const struct ether_hdr *)header_start;
    printf(“[[ Layer 2 :: Ethernet Header ]]\n”);
    printf(“[source: %02x”, ethernet_header->ether_src_addr[0]);
    for(i=1; i < ETHER_ADDR_LEN; i++)
    printf(“:%02x”, ethernet_header->ether_src_addr[i]);
    printf(“\tDest: %02x”, ethernet_header->ether_dest_addr[0]);
    for(i=1; i < ETHER_ADDR_LEN; i++)
    printf(“:%02x”, ethernet_header->ether_dest_addr[i]);
    printf(“\tType: %hu ]\n”, ethernet_header->ether_type);
    }
    void decode_ip(const u_char *header_start) {
    const struct ip_hdr *ip_header;
    ip_header = (const struct ip_hdr *)header_start;
    printf(“\t(( Layer 3 ::: IP Header ))\n”);
    printf(“\t(Source: %s\t”, inet_ntoa(ip_header->ip_src_addr));
    printf(“Dest: %s )\n”, inet_ntoa(ip_header->ip_dest_addr));
    printf(“\t( Type: %u\t”, (u_int) ip_header->ip_type);
    printf(“ID: %hu\tLength: %hu )\n”, ntohs(ip_header->ip_id), ntohs(ip_header->ip_len));
    }
    u_int decode_tcp(const u_char *header_start) {
    u_int header_size;
    const struct tcp_hdr *tcp_header;
    tcp_header = (const struct tcp_hdr *)header_start;
    header_size = 4 * tcp_header->tcp_offset;
    printf(“\t\t{{ Layer 4 :::: TCP Header }}\n”);
    printf(“\t\t{ Src Port: %hu\t”, ntohs(tcp_header->tcp_src_port));
    printf(“Dest Port: %hu }\n”, ntohs(tcp_header->tcp_dest_port));
    printf(“\t\t{ Seq #: %u\t”, ntohl(tcp_header->tcp_seq));
    printf(“Ack #: %u }\n”, ntohl(tcp_header->tcp_ack));
    printf(“\t\t{ Header Size: %u\tFlags: “, header_size);
    if(tcp_header->tcp_flags & TCP_FIN)
    printf(“FIN “);
    if(tcp_header->tcp_flags & TCP_SYN)
    printf(“SYN “);
    if(tcp_header->tcp_flags & TCP_RST)
    printf(“RST “);
    if(tcp_header->tcp_flags & TCP_PUSH)

    0x440 Анализ сетевых пакетов (сниффинг)
    267
    printf(“PUSH “);
    if(tcp_header->tcp_flags & TCP_ACK)
    printf(“ACK “);
    if(tcp_header->tcp_flags & TCP_URG)
    printf(“URG “);
    printf(“ }\n”);
    return header_size;
    }
    Декодирующим функциям передается указатель на начало заголов- ка, который приводится к типу соответствующей структуры. Это дает доступ к разным полям заголовка, но нужно помнить, что все значе- ния представлены в сетевом порядке байтов. Это данные прямо из
    Сети, поэтому их нужно преобразовать к порядку, принятому в архи- тектуре x86.
    reader@hacking:/booksrc $ gcc -o decode_sniff decode_sniff.c -lpcap reader@hacking:/booksrc $ sudo ./decode_sniff
    Sniffing on device eth0
    ==== Got a 75 byte packet ====
    [[ Layer 2 :: Ethernet Header ]]
    [ Source: 00:01:29:15:65:b6 Dest: 00:01:6c:eb:1d:50 Type: 8 ]
    (( Layer 3 ::: IP Header ))
    ( Source: 192.168.42.1 Dest: 192.168.42.249 )
    ( Type: 6 ID: 7755 Length: 61 )
    {{ Layer 4 :::: TCP Header }}
    { Src Port: 35602 Dest Port: 7890 }
    { Seq #: 2887045274 Ack #: 3843058889 }
    { Header Size: 32 Flags: PUSH ACK }
    9 bytes of packet data
    74 65 73 74 69 6e 67 0d 0a | testing..
    ==== Got a 66 byte packet ====
    [[ Layer 2 :: Ethernet Header ]]
    [ Source: 00:01:6c:eb:1d:50 Dest: 00:01:29:15:65:b6 Type: 8 ]
    (( Layer 3 ::: IP Header ))
    ( Source: 192.168.42.249 Dest: 192.168.42.1 )
    ( Type: 6 ID: 15678 Length: 52 )
    {{ Layer 4 :::: TCP Header }}
    { Src Port: 7890 Dest Port: 35602 }
    { Seq #: 3843058889 Ack #: 2887045283 }
    { Header Size: 32 Flags: ACK }
    No Packet Data
    ==== Got a 82 byte packet ====
    [[ Layer 2 :: Ethernet Header ]]
    [Source: 00:01:29:15:65:b6 Dest: 00:01:6c:eb:1d:50 Type: 8 ]
    (( Layer 3 ::: IP Header ))
    ( Source: 192.168.42.1 Dest: 192.168.42.249 )
    ( Type: 6 ID: 7756 Length: 68 )
    {{ Layer 4 :::: TCP Header }}
    { Src Port: 35602 Dest Port: 7890 }

    1   ...   24   25   26   27   28   29   30   31   ...   51


    написать администратору сайта