Я программирую с помощью ‘winpcap’, я читаю файл «.pcap» в своей программе, и после этого я хочу получить IP-адреса пакетов, я написал этот код для получения IP-адресов, вот часть моего кода :
struct sniff_ip {
u_char ip_vhl; /* version << 4 | header length >> 2 */
u_char ip_tos; /* type of service */
u_short ip_len; /* total length */
u_short ip_id; /* identification */
u_short ip_off; /* fragment offset field */
#define IP_RF 0x8000 /* reserved fragment flag */
#define IP_DF 0x4000 /* dont fragment flag */
#define IP_MF 0x2000 /* more fragments flag */
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
u_char ip_ttl; /* time to live */
u_char ip_p; /* protocol */
u_short ip_sum; /* checksum */
struct in_addr ip_src;
struct in_addr ip_dst; /* source and dest address */struct sniff_tcp {
u_short th_sport; /* source port */
u_short th_dport; /* destination port */
u_int32_t th_seq; /* sequence number */
u_int32_t th_ack; /* acknowledgement number */};
и после этого я читаю файл:
while (pcap_next_ex(handler, &header, &packet) >= 0)
{
ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
printf("src port: %d dest port: %d \n", tcp->th_sport, tcp->th_dport);
fprintf(fp,"src port: %d dest port: %d \n", tcp->th_sport, tcp->th_dport);
printf("src address: %s dest address: %s \n", inet_ntoa(ip->ip_src), inet_ntoa(ip->ip_dst));
fprintf(fp,"src address: %s dest address: %s \n", inet_ntoa(ip->ip_src), inet_ntoa(ip->ip_dst));
printf("seq number: %u ack number: %u \n", (unsigned int)tcp-> th_seq, (unsigned int)tcp->th_ack);
fprintf(fp,"seq number: %u ack number: %u \n", (unsigned int)tcp-> th_seq, (unsigned int)tcp->th_ack);
но адреса источника и Ip одинаковы !!! и он неверно печатает порт источника и назначения !! в чем проблема? что мне делать для этого? плз, помогите мне.
Исходный и целевой порты находятся в сетевой порядок байтов (Большой обратный порядок байт). использование ntohs
чтобы получить их в правильном порядке байтов для вашей машины. То же самое касается SEQ и ACK, используйте ntohl
для тех.
Размер заголовка IP не всегда может быть 20, умножьте значение ip_hdr_len
с 4
чтобы получить реальный размер.
Если ваш компилятор поддерживает битовые поля, вы можете использовать их для объявления заголовка IP, чтобы упростить процесс:
struct sniff_ip {
u_char ip_hdr_len:4;
u_char ip_ver:4;
Фиксированный код:
while (pcap_next_ex(handler, &header, &packet) >= 0) {
ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
if (ip->ip_p == 6 /* tcp protocol number */) {
tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + ip->ip_hdr_len * 4);
u_short srcport = ntohs(tcp->th_sport);
u_short dstport = ntohs(tcp->th_dport);
printf("src port: %d dest port: %d \n", srcport, dstport);
char srcname[100];
strcpy(srcname, inet_ntoa(ip->ip_src));
char dstname[100];
strcpy(dstname, inet_ntoa(ip->ip_dst));
printf("src address: %s dest address: %s \n", srcname, dstname);
u_long seq = ntohl(tcp->th_seq);
u_long ack = ntohl(tcp->th_ack);
printf("seq number: %u ack number: %u \n", seq, ack);
}
}
Обратите внимание, что не каждый пакет будет содержать данные TCP, если вы не применили фильтр, используя pcap_compile
а также pcap_setfilter
или можете убедиться, что файл, который вы читаете, содержит только пакеты TCP. Поэтому вы можете проверить значение поля протокола заголовков IP, которое будет 6
(TCP), как видно из приведенного выше кода.
Также обратите внимание, что Wireshark по умолчанию отображает относительные номера SEQ и ACK, поэтому они не будут совпадать с тем, что вы видите там.
Структура упаковки должна быть в порядке.
Как упаковать структуры зависит от вашего компилятора; например, gcc использует #pragma pack()
, Google для pack pragma gcc
или же pack pragma visual c
или какой компилятор вы используете.
Вам нужно сохранить результаты inet_ntoa, если вы дважды вызываете его для printf, как в
char *srcname=strdup(inet_ntoa(ip->ip_src));
char *dstname=strdup(inet_ntoa(ip->ip_dst));
printf("src address: %s dest address: %s \n", srcname, dstname);
free(srcname);
free(dstname);
(Обратите внимание, что вы должны добавить проверку ошибок, указатели результата функции могут быть NULL).
inet_ntoa () возвращает строку точек и чисел в статическом буфере, который перезаписывается при каждом вызове функции.