WinDivert problem - redirecting DNS back to itself on windows

I looked at the basil00 torwall, and for fun they tried to trick it, just to intercept the DNS. (provide answer to me 127.0.0.1 for web filters, training project)

however, at this point I captured the dns package, but it does not return the correct address. for each "blocked" domain it is different.

For example, I put cbc.ca in my hosts.deny file (blacklist) and returns the address 0.4.114.2

then slashdot blacklist, it will return 0.4.0.1

it was rather confusing and disappointing, and after three days of research I got out of ideas.

Here is the code for the redirection part of my program, which seems to be where everything goes against it. (note that some of the comments will be dumb as I hacked into a program for another purpose and have not cleared it yet)

#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;
}

Here's the domain search scope (basically just a hack to try to get the results I want:

#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;
}

any help is appreciated.

Also, I downloaded the code: Github

Thank.

+4
source share
1 answer

Here one of two things happens. You do not read and write DNS packets correctly, and you cannot convert addresses to or from a host into a network order before working with them.

, DNS-. WinDivert, DNS- DNS-, , , , , DNS .

: DNS C/++, , (, . ). , ++ WinDivert, Arsoft.Tools.Net # DNS- DNS- DNS.

++ , boost:: net:: dns, , , , , , . , A Bugged out , , . , , , .

gander , DNS- ++. , 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-.

+2

All Articles