0

我最近发现了原始套接字,我目前正在尝试捕获使用主机命令发送的 DNS 数据包(使用 libcap 库),并在 DNS 服务器之前以错误的地址回复它。不幸的是,它似乎不起作用......我可以使用 tcpdump 看到我的数据包,但它没有传递给主机命令。这是我用于测试的代码:

#include <arpa/inet.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <net/if.h>

#include <sys/socket.h>
#include <sys/types.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>

typedef unsigned int u_int;
typedef unsigned short u_short;
typedef unsigned char u_char;

#include <pcap/pcap.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

struct dnshdr {
    uint16_t id;
    uint16_t flags;
    uint16_t nquestion;
    uint16_t nanswer;
    uint16_t nauthority;
    uint16_t nadditional;
};

struct msg {
    struct ethhdr eth;
    struct iphdr ip;
    struct udphdr udp;
    struct dnshdr dns;
    unsigned char * data;
    size_t dlen;
};

static void msg_init(uint8_t *, struct pcap_pkthdr const *, uint8_t const *);
static void msg_to_pkt(struct msg *, unsigned char *);
static uint16_t msg_len(struct msg *);
static struct msg * msg_cap(void);


static void
msg_init(uint8_t * msg, struct pcap_pkthdr const * h, uint8_t const * bytes)
{
    struct msg * self;
    size_t offset;

    self = (struct msg *)msg;
    offset = 0;
    memcpy(&self->eth, bytes + offset, sizeof self->eth);
    offset += sizeof self->eth;
    memcpy(&self->ip, bytes + offset, sizeof self->ip);
    offset += self->ip.ihl * 4;
    memcpy(&self->udp, bytes + offset, sizeof self->udp);
    offset += sizeof self->udp;
    memcpy(&self->dns, bytes + offset, sizeof self->dns);
    offset += sizeof self->dns;
    self->dlen = h->len - offset;
    self->data = malloc(self->dlen);
    if (self->data == NULL) {
        perror("malloc");
        free(self);
        exit(EXIT_FAILURE);
    }
    memcpy(self->data, bytes + offset, self->dlen); 
}


static void
msg_to_pkt(struct msg * self, unsigned char * pkt)
{
    memcpy(pkt, &self->eth, sizeof self->eth);
    pkt += sizeof self->eth;
    memcpy(pkt, &self->ip, sizeof self->ip);
    pkt += sizeof self->ip;
    memcpy(pkt, &self->udp, sizeof self->udp);
    pkt += sizeof self->udp;
    memcpy(pkt, &self->dns, sizeof self->dns);
    pkt += sizeof self->dns;
    memcpy(pkt, self->data, self->dlen);
}


static uint16_t
msg_len(struct msg * self)
{
    uint16_t len;

    len = 0;
    len += sizeof self->eth;
    len += sizeof self->ip;
    len += sizeof self->udp;
    len += sizeof self->dns;
    len += self->dlen;
    return len;
}


static struct msg *
msg_cap(void)
{
    struct bpf_program filter;
    pcap_t * p;
    void * msg;
    char errbuf[PCAP_ERRBUF_SIZE];

    p = pcap_create("eth0", errbuf);
    if (p == NULL) {
        pcap_perror(p, "pcap_create");
        return NULL;
    }
    if (pcap_activate(p) < 0) {
        pcap_perror(p, "pcap_activate");
        return NULL;
    }
    if (pcap_compile(p, &filter, "dst port 53 and udp", 1,
    PCAP_NETMASK_UNKNOWN) < 0) {
        pcap_perror(p, "pcap_compile");
        return NULL;
    }
    if (pcap_setfilter(p, &filter) < 0) {
        pcap_perror(p, "pcap_setfilter");
        return NULL;
    }
    msg = malloc(sizeof (struct msg));
    if (msg == NULL) {
        perror("malloc");
        return NULL;
    }
    if (pcap_loop(p, 1, &msg_init, msg) < 0) {
        pcap_perror(p, "pcap_loop");
        free(msg);
        return NULL;
    }
    return msg;
}


uint16_t
cksum(void const * buf, size_t nbytes)
{
    uint32_t cs = 0;
    uint16_t const * p;

    p = buf;
    while (nbytes > 1) {
        cs += *p++;
        nbytes -= 2;
    }
    if (nbytes == 1) {
        cs += *(uint8_t *)p;
    }
    while ((cs >> 16) != 0) {
        cs = (cs & 0xFFFF) + (cs >> 16);
    }
    return ~(uint16_t)cs;
}


int
main(void)
{
    struct msg * msg;
    struct sockaddr_ll sll;
    char resp[] = "\xc0\x0c\0\1\0\1\0\0\x02\x65\0\4\x7f\0\0\1";
    char ethaddr[ETH_ALEN];
    unsigned char * tmp;
    size_t const rlen = sizeof resp - 1;
    size_t mlen;
    uint32_t ipaddr;
    uint16_t port;
    int s;

    s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
    if (s < 0) {
        perror("socket");
        return EXIT_FAILURE;
    }
    msg = msg_cap();
    if (msg == NULL) {
        return EXIT_FAILURE;
    }

    /* add DNS response */
    tmp = malloc(msg->dlen + rlen);
    if (tmp == NULL) {
        perror("malloc");
        return EXIT_FAILURE;
    }
    memcpy(tmp, msg->data, msg->dlen);
    memcpy(tmp + msg->dlen, resp, rlen);
    free(msg->data);
    msg->data = tmp;
    msg->dlen += rlen;
    mlen = msg_len(msg);

    /* reverse Ethernet address */
    memcpy(ethaddr, msg->eth.h_dest, ETH_ALEN);
    memcpy(msg->eth.h_dest, msg->eth.h_source, ETH_ALEN);
    memcpy(msg->eth.h_source, ethaddr, ETH_ALEN);

    /* reverse IP address and change some fields */
    ipaddr = msg->ip.daddr;
    msg->ip.daddr = msg->ip.saddr;
    msg->ip.saddr = ipaddr;
    msg->ip.tos = 0;
    msg->ip.ttl = 53;
    msg->ip.id = htons(9999);
    msg->ip.tot_len = htons(mlen - sizeof msg->eth);
    msg->ip.check = 0;
    msg->ip.check = cksum(&msg->ip, sizeof msg->ip);

    /* reverse UDP ports and change some fields */
    port = msg->udp.dest;
    msg->udp.dest = msg->udp.source;
    msg->udp.source = port;
    msg->udp.len = htons(mlen - sizeof msg->eth - sizeof msg->ip);
    msg->udp.check = 0;

    /* change DNS flags and nanswer fields */
    msg->dns.flags = htons(0x8180);
    msg->dns.nanswer = htons(1);

    /* alloc buffer and send the packet */
    tmp = malloc(mlen);
    if (tmp == NULL) {
        perror("malloc");
        return EXIT_FAILURE;
    }
    msg_to_pkt(msg, tmp);
    sll.sll_family = AF_PACKET;
    sll.sll_protocol = htons(ETH_P_IP);
    sll.sll_ifindex = if_nametoindex("eth0");
    sll.sll_hatype = 1;
    sll.sll_pkttype = PACKET_HOST;
    sll.sll_halen = ETH_ALEN;
    memcpy(&sll.sll_addr, msg->eth.h_dest, ETH_ALEN);
    if (sendto(s, tmp, mlen, 0, (struct sockaddr *)&sll, sizeof sll) < 0) {
        perror("sendto");
        return EXIT_FAILURE;
    }
    close(s);
    return 0;
}

有什么问题吗?

4

1 回答 1

0

好的,经过进一步的测试和头痛,我终于找到了问题的根源。事实上,有两个问题。

首先,我在想当该sll_pkttype字段设置为PACKET_HOST它意味着数据包被发送到本地主机。但是,在回读数据包(7)之后,该字段似乎仅在接收数据包时使用。换句话说,我的数据包被发送到我的路由器并被丢弃,因为 MAC 目标地址错误。

接下来,即使我更改了 MAC 目标地址,数据包也不会从我的路由器发送回我的计算机。这是一个 NAPT 问题。因为我捕获的数据包在我的路由器的 NAT 表中打开了一个 UDP 会话,所以我无法发送一个数据包,其中我的计算机地址作为目标 IP 地址,并且目标端口与 NAT 中保存的端口相同桌子。因此,我将目标地址更改为路由器的外部地址,它就成功了!

于 2013-05-11T10:30:38.477 回答