WinDivert问题 – 在Windows上将DNS重定向回自我

我正在看看basil00的torwall,为了好玩而试图削减它只是为了拦截DNS. (为网络过滤目的,学习项目提供回复127.0.0.1的答案)

但是,在这一点上,我有它劫持dns数据包,但它没有返回正确的地址.对于每个“被阻止”的域名,它都是不同的.

例如,我将cbc.ca放入我的hosts.deny文件(黑名单)中,并返回地址0.4.114.2

然后将slashdot列入黑名单,它将返回0.4.0.1

这一直令人困惑和令人沮丧,经过三天的研究,我没有想法.

这是我的程序的重定向部分的代码,这似乎是出错的地方.
(注意一些评论会很愚蠢,因为我为了一个不同的目的而破解了一个程序并且还没有清理它)

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>


#include "windivert.h"

#include "domain.h"
#include "main.h"
#include "redirect.h"

#define MAX_PACKET          4096
#define NUM_WORKERS         4

// DNS headers
#define DNS_MAX_NAME    254
struct dnshdr
{
    uint16_t id;
    uint16_t options;
    uint16_t qdcount;
    uint16_t ancount;
    uint16_t nscount;
    uint16_t arcount;
} __attribute__((__packed__));

struct dnsq
{
    uint16_t type;
    uint16_t class;
} __attribute__((__packed__));

struct dnsa
{
    uint16_t name;
    uint16_t type;
    uint16_t class;
    uint32_t ttl;
    uint16_t length;
    uint32_t addr;
} __attribute__((__packed__));


static DWORD redirect_worker(LPVOID arg);
static int handle_dns(HANDLE handle, PWINDIVERT_ADDRESS addr,
    PWINDIVERT_IPHDR iphdr, PWINDIVERT_UDPHDR udphdr, char *data,
    size_t data_len);


// State:
static bool redirect_on = false;
static HANDLE handle = INVALID_HANDLE_VALUE;
static HANDLE workers[NUM_WORKERS] = {NULL};    // Worker threads



// Send a packet asynchronously:
static void send_packet(HANDLE handle, void *packet, size_t packet_len,
    PWINDIVERT_ADDRESS addr)
{
    addr->Direction = WINDIVERT_DIRECTION_INBOUND;
    WinDivertHelperCalcChecksums(packet, packet_len, 0);
    if (!WinDivertSend(handle, packet, packet_len, addr, NULL))
        debug("Send packet failed (err=%d)\n", (int)GetLastError());
}


// Start traffic redirect through Tor:
extern void redirect_start(void)
{
    debug("DNS divert START\n");

    if (handle != INVALID_HANDLE_VALUE)
        return;

    handle = WinDivertOpen(
        "outbound and udp.DstPort == 53 or inbound and udp.DstPort = 53", 0, 0, 0);



    // Launch threads:
    redirect_on = true;
    for (size_t i = 0; i < NUM_WORKERS; i++)
    {
        workers[i] = CreateThread(NULL, MAX_PACKET*3,
            (LPTHREAD_START_ROUTINE)redirect_worker, (LPVOID)handle, 0, NULL);
        if (workers[i] == NULL)
        {
            exit(EXIT_FAILURE);
        }
    }
}

// Stop traffic redirect through Tor:
extern void redirect_stop(void)
{
    debug("DNS divert STOP\n");

    if (handle == INVALID_HANDLE_VALUE)
        return;

    // Close the WinDivert handle; will cause the workers to exit.
    redirect_on = false;
    if (!WinDivertClose(handle))
    {
        exit(EXIT_FAILURE);
    }
    handle = INVALID_HANDLE_VALUE;

    for (size_t i = 0; i < NUM_WORKERS; i++)
    {
        WaitForSingleObject(workers[i], INFINITE);
        workers[i] = NULL;
    }

}

// Redirect worker thread:
static DWORD redirect_worker(LPVOID arg)
{
    HANDLE handle = (HANDLE)arg;

    // Packet processing loop:
    char packet[MAX_PACKET];
    UINT packet_len;
    WINDIVERT_ADDRESS addr;

    while (redirect_on)
    {
        if (!WinDivertRecv(handle, packet, sizeof(packet), &addr, &packet_len))
        {
            // Silently ignore any error.
            continue;
        }

        PWINDIVERT_IPHDR iphdr = NULL;
        PWINDIVERT_TCPHDR tcphdr = NULL;
        PWINDIVERT_UDPHDR udphdr = NULL;
        PVOID data = NULL;
        UINT data_len;
        WinDivertHelperParsePacket(packet, packet_len, &iphdr, NULL, NULL,
            NULL, &tcphdr, &udphdr, &data, &data_len);

        int dnshandle = 0;
        if (udphdr != NULL && ntohs(udphdr->DstPort) == 53)
            dnshandle = handle_dns(handle, &addr, iphdr, udphdr, data, data_len);


        if(dnshandle != 1)
        {
            if (!WinDivertSend(handle, packet, packet_len, &addr, NULL))
            {


            }
        }
    }
    return 0;
}



// Handle DNS requests.
// NOTES:
// - If anything goes wrong, we simply drop the packet without error.
// - An alternative approach would be to let Tor resolve the address, however,
//   this would be slow.
static int handle_dns(HANDLE handle, PWINDIVERT_ADDRESS addr,
    PWINDIVERT_IPHDR iphdr, PWINDIVERT_UDPHDR udphdr, char *data,
    size_t data_len)
{

    struct dnshdr *dnshdr = (struct dnshdr *)data;
    data += sizeof(struct dnshdr);
    data_len -= sizeof(struct dnshdr);

    char name[DNS_MAX_NAME + 8];            // 8 bytes extra.
    size_t i = 0;
    while (i < data_len && data[i] != 0)
    {
        size_t len = data[i];
        if (i + len >= DNS_MAX_NAME)
            return -1;
        name[i++] = '.';
        for (size_t j = 0; j < len; j++, i++)
            name[i] = data[i];
    }

    name[i++] = '\0';

    // Generate a fake IP address and associate it with this domain name:
    uint32_t fake_addr = domain_lookup_addr(name);
    if (fake_addr == 0)
    {

        // This domain is blocked; so ignore the request.
        // Construct a query response:
    size_t len = sizeof(struct dnshdr) + data_len + sizeof(struct dnsa);
    if (len > 512)                          // Max DNS packet size.
        return -1;
    len += sizeof(WINDIVERT_IPHDR) + sizeof(WINDIVERT_UDPHDR) + len;

    char buf[len + 8];                      // 8 bytes extra.
    PWINDIVERT_IPHDR riphdr = (PWINDIVERT_IPHDR)buf;
    PWINDIVERT_UDPHDR rudphdr = (PWINDIVERT_UDPHDR)(riphdr + 1);
    struct dnshdr *rdnshdr = (struct dnshdr *)(rudphdr + 1);
    char *rdata = (char *)(rdnshdr + 1);


    UINT local_ip;
    DivertHelperParseIPv4Address("127.0.0.1",&local_ip);

    memset(riphdr, 0, sizeof(WINDIVERT_IPHDR));
    riphdr->Version   = 4;
    riphdr->HdrLength = sizeof(WINDIVERT_IPHDR) / sizeof(uint32_t);
    riphdr->Length    = htons(len);
    riphdr->Id        = htons(0xF00D);
    WINDIVERT_IPHDR_SET_DF(riphdr, 1);
    riphdr->TTL       = 64;
    riphdr->Protocol  = IPPROTO_UDP;
    riphdr->SrcAddr   = iphdr->DstAddr;
    riphdr->DstAddr   = iphdr->SrcAddr;

    memset(rudphdr, 0, sizeof(WINDIVERT_UDPHDR));
    rudphdr->SrcPort  = htons(53);          // DNS
    rudphdr->DstPort  = udphdr->SrcPort;
    rudphdr->Length   = htons(len - sizeof(WINDIVERT_IPHDR));

    rdnshdr->id = dnshdr->id;
    rdnshdr->options = htons(0x8180);       // Standard DNS response.
    rdnshdr->qdcount = htons(0x0001);
    rdnshdr->ancount = htons(0x0001);
    rdnshdr->nscount = 0;
    rdnshdr->arcount = 0;

    memcpy(rdata, data, data_len);
    struct dnsa *rdnsa = (struct dnsa *)(rdata + data_len);
    rdnsa->name   = htons(0xC00C);
    rdnsa->type   = htons(0x0001);          // (A)
    rdnsa->class  = htons(0x0001);          // (IN)
    rdnsa->ttl    = htonl(0x00000258) ;              // 1 second
    rdnsa->length = htons(0x0004);
    rdnsa->addr   = htonl(local_ip);       // Fake address

    send_packet(handle, &buf, len, addr);



    debug("address: %u\n",addr->Direction);
    debug("Intercept DNS %s\n", (name[0] == '.'? name+1: name));
    return 1;
    }
    // Re-inject the matching packet.


    /*
    /
    */
    return 0;
}

这是它的域查找方面(大多数情况下只是为了获得我想要的结果而被黑客攻击:

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

#include "domain.h"
#include "main.h"

#define RATE_LIMIT                  8000

#define rand16()                    \
    (rand() & 0xFF) | ((rand() & 0xFF) << 8)

// Domain blacklist:
struct blacklist
{
    size_t size;
    size_t len;
    char **names;
};
static struct blacklist *blacklist = NULL;

// State:
static struct name *names[UINT16_MAX] = {NULL};

static HANDLE names_lock = NULL;

// Prototypes:
static struct blacklist *domain_blacklist_read(const char *filename);
static bool domain_blacklist_lookup(struct blacklist *blacklist,
    const char *name);
static int __cdecl domain_blacklist_compare_0(const void *x, const void *y);
static int domain_blacklist_compare(const char *name0, size_t len,
    const char *name1);



// Initialize this module:
extern void domain_init(void)
{
    // Load the domain blacklist.
    blacklist = domain_blacklist_read("hosts.deny");

}

// Lookup an address given a domain name.  If the name does not exist then
// create one.
extern uint32_t domain_lookup_addr(const char *name0)
{
    if (name0[0] == '.')
        name0++;

    if (domain_blacklist_lookup(blacklist, name0))
    {
        debug("Block %s\n", name0);

        return 0;       // Blocked!
    }
    return;
}



// Read the blacklist file:
static struct blacklist *domain_blacklist_read(const char *filename)
{
    struct blacklist *blacklist =
        (struct blacklist *)malloc(sizeof(struct blacklist));
    if (blacklist == NULL)
    {

        exit(EXIT_FAILURE);
    }
    blacklist->size = 0;
    blacklist->len = 0;
    blacklist->names = NULL;

    FILE *stream = fopen(filename, "r");
    if (stream == NULL)
    {
        return blacklist;
    }

    // Read blocked domains:
    int c;
    char buf[256];
    while (true)
    {
        while (isspace(c = getc(stream)))
            ;
        if (c == EOF)
            break;
        if (c == '#')
        {
            while ((c = getc(stream)) != '\n' && c != EOF)
                ;
            continue;
        }
        size_t i = 0;
        while (i < sizeof(buf)-1 && (c == '-' || c == '.' || isalnum(c)))
        {
            buf[i++] = c;
            c = getc(stream);
        }
        if (i >= sizeof(buf)-1 || !isspace(c))
        {

            exit(EXIT_FAILURE);
        }
        buf[i] = '\0';
        if (blacklist->len >= blacklist->size)
        {
            blacklist->size = (blacklist->size == 0? 32: 2 * blacklist->size);
            blacklist->names = (char **)realloc(blacklist->names,
                blacklist->size * sizeof(char *));
            if (blacklist->names == NULL)
            {

                exit(EXIT_FAILURE);
            }
        }
        size_t size = (i+1) * sizeof(char);
        char *name = (char *)malloc(size);
        if (name == NULL)
        {

            exit(EXIT_FAILURE);
        }
        for (size_t j = 0; j < i; j++)
            name[j] = buf[i - 1 - j];
        name[i] = '\0';
        blacklist->names[blacklist->len++] = name;
    }

    fclose(stream);

    qsort(blacklist->names, blacklist->len, sizeof(char *),
        domain_blacklist_compare_0);
    return blacklist;
}

// Check if a domain matches the blacklist or not:
static bool domain_blacklist_lookup(struct blacklist *blacklist,
    const char *name)
{
    if (blacklist->len == 0)
        return false;

    size_t len = strlen(name);
    ssize_t lo = 0, hi = blacklist->len-1;
    while (lo <= hi)
    {
        ssize_t mid = (lo + hi) / 2;
        int cmp = domain_blacklist_compare(name, len, blacklist->names[mid]);
        if (cmp > 0)
            hi = mid-1;
        else if (cmp < 0)
            lo = mid+1;
        else
            return true;
    }
    return false;
}

// Domain compare function(s):
static int __cdecl domain_blacklist_compare_0(const void *x, const void *y)
{
    const char *name0 = *(const char **)x;
    const char *name1 = *(const char **)y;
    return strcmp(name0, name1);
}
static int domain_blacklist_compare(const char *name0, size_t len,
    const char *name1)
{
    size_t i = 0;
    ssize_t j = (ssize_t)len - 1;
    for (; j >= 0 && name1[i] != '\0'; i++, j--)
    {
        int cmp = (int)name1[i] - (int)name0[j];
        if (cmp != 0)
            return cmp;
    }
    if (j < 0 && name1[i] != '\0')
        return 1;
    return 0;
}

任何协助表示赞赏.

另外,我已将代码上传到:Github

谢谢.

最佳答案 这里有两件事之一.您正在错误地读取和写入DNS数据包,或者您在使用它们之前未能将地址转换为主机或从主机转换为网络顺序.

我打赌你会错误地读取和写入DNS数据包.我使用WinDivert实现了自己的过滤系统,在那里我劫持并通过我自己的本地DNS服务器传递所有DNS流量,我看到这些捏造的地址与我在解析和编写DNS时获得的结果完全一致包错误.

坏消息是,我不能指向你在C/C++中的完整DNS库,因为我知道没有任何实际上使工作更容易(可能,请参阅编辑).我个人使用WinDivert用C语言编写了我的转移代码,然后使用Arsoft.Tools.Net C#DNS库实际运行本地DNS服务器并操纵DNS响应.

C中有一个项目叫做boost :: net :: dns,因为我认为作者希望它能成为boost的一部分,它不是也可能不会.避免使用这个库作为解析和存储多个A记录的内部机制被删除和破坏,你将得到你在这里得到的相同的古怪结果.我试图与他合作以解决问题,但他只对责备我的代码感兴趣.

如果我能找到一个在C中使用DNS数据包的合适的lib,我将再次有一个gander并更新我的答案.不要试图自己做,我们正在讨论整个RFC协议书的整个协议,以便自己正确地做到这一点.

更新

正如所承诺的,以下是我搜索的一些结果:

Mozilla Necko(原名Netlib)
http://mxr.mozilla.org/mozilla-central/source/netwerk/dns/

C-战神:
https://github.com/bagder/c-ares

编译的C-Ares替代库列表:
http://c-ares.haxx.se/otherlibs.html

此外,它是特定于Linux但相当干净的代码.您可以通过libcrafter的DNS类进行窃取并将它们移动到您自己的项目中.

https://github.com/pellegre/libcrafter/blob/master/libcrafter/crafter/Protocols/DNS.h

同样,除非您的“学习项目”理解并重新创建互联网的基础支柱之一,否则不要自己实施.尝试并使用之前列出的库之一来处理DNS数据包.

点赞