【问题标题】:Parsing a TCP Packet data解析 TCP 数据包数据
【发布时间】:2015-02-02 11:00:24
【问题描述】:

我正在尝试解析一个 tcp 数据包,然后分配一个指向有效负载开头的指针。

我正在使用 C,这是我目前的代码:

void dump(const unsigned char *data, int length) { //*data contains the raw packet data
    unsigned int i;
    static unsigned long pcount = 0;

    // Decode Packet Header
    struct ether_header *eth_header = (struct ether_header *) data;

    printf("\n\n === PACKET %ld HEADER ===\n", pcount);

    printf("\nSource MAC: ");
    for (i = 0; i < 6; ++i) {
        printf("%02x", eth_header->ether_shost[i]); //? Why don't i use nthos here?
        if (i < 5) printf(":");
    }

    unsigned short ethernet_type = ntohs(eth_header->ether_type);
    printf("\nType: %hu\n", ethernet_type);

    if (ethernet_type == ETHERTYPE_IP) { //IP Header
        printf("\n  == IP HEADER ==\n");
        struct ip *ip_hdr = (struct ip*) data + sizeof(struct ether_header);
        unsigned int size_ip = ip_hdr->ip_hl * 4;
        printf("\nIP Version: %u", ip_hdr->ip_v); //? Nthos or no nthos
        printf("\nHeader Length: %u", ip_hdr->ip_hl); //? Nthos or no nthos
        printf("\nTotal Length: %hu", ntohs(ip_hdr->ip_len)); //? Nthos or no nthos

        // TCP Header
        printf("\n== TCP HEADER ==\n");
        struct tcphdr *tcp_hdr = (struct tcphdr*) data + sizeof(struct ether_header) + size_ip;
        printf("\n Source Port: %" PRIu16, nthos(tcp_hdr->th_sport));
        printf("\n Destination Port: %" PRIu16, nthos(tcp_hdr->th_dport));
        printf("\n fin: %" PRIu16, tcp_hdr->fin);
        printf("\n urg: %" PRIu16, tcp_hdr->urg);
        printf("\n ack_seq: %" PRIu32, ntohl(tcp_hdr->ack_seq));

        //Transport payload! i.e. rest of the data
        const unsigned char *payload = data + ETH_HLEN + size_ip + sizeof(struct tcphdr) + tcp_hdr->doff;

    }

我确定这段代码有错误,因为端口号都很奇怪。没有一个分配给 80。输出的 Ip 版本也可能非常奇怪(如版本 11)。我究竟做错了什么?谢谢!

我也不确定何时使用 nthos,何时不使用。我知道 nthos 用于 16 位无符号整数,我知道 nthol 用于 32 位无符号整数,但我知道您并不打算将它们用于这些数据包中的所有内容(例如:tcp_hdr->fin)。为什么是某些事情而不是他们?

非常感谢!

编辑:

感谢 Art 解决了大部分问题。我编辑了我的 tcp_hdr 和 ip_hdr 所以括号现在是正确的!

我还有两个问题:

  • 有效载荷的前 10 个字节有奇怪的符号(所以我认为我没有正确分配有效载荷)。
  • 我仍然不确定何时使用 nthos/nthol。我知道 u_int16_t 是 ntohs 而 u_int32_t 是 ntohl。但是那些有符号整数或无符号短整数的东西呢?例如,我没有使用 ntohs 或 nthol 来让 ip_v 工作。为什么不?是“ip_hdr->ip_hl”吗?等等……

EDIT2

我已经修复了为什么我的有效负载没有正确输出(这是因为我计算的 TCP_header 大小错误)。

虽然我仍然对何时使用 nthos 感到困惑,但我将把它作为一个单独的问题提出,因为我认为我在这 1 个帖子上问了太多问题!

When to use ntohs and ntohl in C?

【问题讨论】:

  • 你知道端口号之类的东西是按network字节顺序排列的吗?使用ntohs 将短(16 位)值从网络字节顺序转换为主机字节顺序。
  • 我都试过了,但是 eitehr 的数字很奇怪。无论如何现在更新代码!
  • 还有其他看起来正确的字段吗?喜欢源/目标地址?以太网头?与例如相同的数据包相比,它看起来如何线鲨?而您确实在原始套接字上接收到这个?并接收所有的数据包?

标签: c tcp packet packet-capture packet-sniffers


【解决方案1】:

你的问题很可能在这里:

struct ip *ip_hdr = (struct ip*) data + sizeof(struct ether_header);
struct tcphdr *tcp_hdr = (struct tcphdr*) data + sizeof(struct ether_header) + size_ip;

执行(struct ip*) data + sizeof(struct ether_header) 首先将数据转换为struct ip *,然后将sizeof(struct ether_header) 添加到它,我们从指针算术中知道这不会像您期望的那样做。

如果问题仍然不清楚,这里有一个简单的程序可以为您指明正确的方向:

#include <stdio.h>

struct foo {
    int a, b;
};

int
main(int argc, char **argv)
{
    char *x = NULL;

    printf("%p\n", x);
    printf("%p\n", (struct foo *)x + 4);
    printf("%p\n", (struct foo *)(x + 4));

    return 0;
}

【讨论】:

  • 几乎所有问题都已解决。你介意你是否也回答我问题的第二部分,即何时使用 ntohs。我目前制定的规则是任何类型的 u_int16_t 是 ntohs 和 u_int32_t 是 ntohl。但是那些有符号整数或无符号短整数的东西呢?例如,我没有使用 ntohs 来让 ip_v 工作。为什么不?谢谢
  • 查看ip头和定义。基本规则是:不要转换 1B 或更短的值(ip_v、ip_hl)。您将 ntohs 用于 2B 值(ip_len、ip_p)。您将 ntohl 用于 4B 值。 “但是那些有符号整数或无符号短整数的东西呢”:它是否有符号并不重要(而且我认为ip hdr中没有任何有符号值),所以有符号整数只是4B值unsigned short 是 2B 值。你可以在这里看到ip头:en.wikipedia.org/wiki/IPv4#Header
  • 所以等待 "ip_hdr->ip_hl" 使用 htol 然后因为它是 "unsigned int ip_hl:4;" 类型当我添加 htol 时,程序甚至在读取 TCP 标头之前终止(可能意味着它读取了它不应该读取的一部分内存)。然而,当我离开它时没有 ntohl 或 ntohs 它工作得很好!
  • 不,它没有。 ip_hl 只有 4 位。仅当您的值长度为 2 个字节或更长时,谈论字节顺序才有意义。如果您有一个 1 字节的变量,则它在网络字节顺序和主机字节顺序中是相同的(如果您更改 1 字节的顺序,它不会改变)。
  • 为了澄清你的例子:我不知道你有什么 ip.h,但我说 u_char ip_hl:4 (这是 1 个字节)。如果你说的是 unsigned int,那只能意味着 unsigned int ip_tos:8, unsigned int ip_len:16 紧随其后。如果您在 ip_hl 上调用 ntohl,它会将您的 4 位值视为 32 位,因此它会处理我之前提到的所有值,因此它完全改变了标头的含义。
猜你喜欢
  • 2012-12-21
  • 2020-05-18
  • 1970-01-01
  • 2016-04-19
  • 2011-05-17
  • 1970-01-01
  • 2011-01-12
  • 2015-06-15
  • 2013-05-23
相关资源
最近更新 更多