/*
 * Raw Covert: an IEEE 802.11 Covert Channel
 *
 * Copyright (c) 2005-2006, Laurent Butti
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */

/*
 *
 * This tool is a small proof-of-concept code. It aims at encoding a
 * covert channel in valid ACK frames in the RA address field. Using
 * ACK frames has the advantage to be quite stealthy as they are
 * considered harmless and thus are generally not analyzed by wireless
 * IDS.
 * Covert channel principles can be extended to encode anything between
 * the lines in the 802.11 protocol and to achieve a reliable
 * communication (shell, file transfer...).
 * 
 * Do not blame me for ugly coding style, it is only a proof of concept!
 *
*/

#include <stdint.h>
#include <sys/ioctl.h>
#include <netpacket/packet.h>
#include <stdio.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <pcap.h>
#include <getopt.h>

int close(int sock);
int usage(char *arg0);

char *author = "Laurent Butti";
const char *version = "0.1";

#define PRISM_HEADER 144

#define ARPHRD_IEEE80211 801
#define ARPHRD_IEEE80211_PRISM 802

/* Structures */
typedef uint64_t u64;
typedef uint32_t u32;
typedef uint16_t u16;
typedef uint8_t u8;

int isfcs = 0;
int isprism = 0;
int ctrl_c = 0;
int sock = 0;
u_char *p;
pcap_t *pcap;
char err[PCAP_ERRBUF_SIZE];
char *ifname = NULL;
u16 magic_number = 0x6666;
u8 buf[33], payload[33];
int offset = 0;

/* ACK frames */
u8 ack_send[14];
u8 ack_recv[14];

static void sighandler (int sig) {
    if (sig == SIGINT) {
        ctrl_c++;
        fprintf(stderr, "Info\t: User interrupt\n");
    }
}

static int open_sock (char *ifname, int sock) {
    struct sockaddr_ll ll_addr;
    struct ifreq ifr;

    sock = socket (PF_PACKET, SOCK_RAW, ETH_P_ALL);
    if (sock == -1) {
        fprintf(stderr, "Error\t: Socket failed\n");
        return (-1);
    }

    ll_addr.sll_family = AF_PACKET;
    ll_addr.sll_protocol = 0;
    ll_addr.sll_halen = ETH_ALEN;

    strncpy(ifr.ifr_name, ifname, IFNAMSIZ);

    if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0) {
        fprintf(stderr, "Error\t: ioctl(SIOCGIFINDEX) failed\n");
        return (-1);
    }

    ll_addr.sll_ifindex = ifr.ifr_ifindex;

    if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) {
        fprintf(stderr, "Error\t: ioctl(SIOCGIFHWADDR) failed\n");
        return (-1);
    }

    memcpy(ll_addr.sll_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);

    if (ifr.ifr_hwaddr.sa_family != ARPHRD_IEEE80211 &&
            ifr.ifr_hwaddr.sa_family != ARPHRD_IEEE80211_PRISM) {
        fprintf( stderr, "Error\t: bad linktype\n");
        return (-1);
    }

    if (bind (sock, (struct sockaddr *)&ll_addr, sizeof(ll_addr)) == -1) {
        fprintf(stderr, "Error\t: bind failed\n");
        close(sock);
        return (-1);
    }

    return sock;
}


void pcap_callback (u_char *p, const struct pcap_pkthdr *pkthdr, const u_char *packet_data) {
    /* Check for ACK frames */
    if (packet_data[0 + isprism * PRISM_HEADER] == 0xD4) {
        memcpy(ack_recv, &packet_data[0 + isprism * PRISM_HEADER], 10);

        /* Check for magic number */
        if (memcmp(ack_recv + 4, &magic_number, 2) != 0)
            return ;

        /* Check for covert channel */
        if ((ack_recv[6] == 0x00) || (offset > 32)) {
            buf[offset] ='\0';
            fprintf(stderr, "Info\t: String received > %s\n", buf);
            offset = 0;
        } else {
            buf[offset++] = ack_recv[6];
        }
    }
    return;
}

int covert_client(void) {
    memcpy(ack_send, "\xD4\x00\x00\x00", 4);
    memset(ack_send + 4, 1, 10);

    fprintf(stderr, "Info\t: Operating in client mode\n");

    while (ctrl_c == 0) {
        int i = 0;
        memset(payload, 0, 33);
        /* Get string from user */
        printf("> ");
        fgets(payload, sizeof(payload), stdin);

        while ((payload[i] != 0x00) && (i < 33)) {
            /* Construct ACK frame */
            memcpy(ack_send + 4, &magic_number, 2);
            memcpy(ack_send + 6, payload + i, 1);

            /* Send the ACK */
            send(sock, ack_send, 10, 0);
            i++;
        }

        ack_send[6] = 0x00;
        send(sock, ack_send, 14, 0);

        fprintf(stderr, "Info\t: String sent\n");
    }
    return 1;
}

int covert_server(void) {

    memcpy(ack_recv, "\xD4\x00\x00\x00", 4);
    memset(ack_recv + 10, 0, 4);

    pcap = pcap_open_live(ifname, 512, 1, 1, err);

    if (pcap == NULL) {
        fprintf(stderr, "Error\t: pcap_open_live failed (%s)\n", err);
        return -1;
    }

    pcap_setnonblock(pcap, 1, err);

    /* Check datalink for prism headers or not */
    switch (pcap_datalink(pcap)) {
    case DLT_PRISM_HEADER:
        fprintf(stderr, "Info\t: PRISM datalink type\n");
        isprism = 1;
        break;
    case DLT_IEEE802_11:
        fprintf(stderr, "Info\t: IEEE802_11 datalink type\n");
        isprism = 0;
        break;
    default:
        fprintf(stderr, "Error\t: Unknown datalink type\n");
        return -1;
    }

    fprintf(stderr, "Info\t: Operating in server mode\n");

    /* Main loop for listening */
    while (ctrl_c == 0) {
        signal(SIGINT, sighandler);
        pcap_dispatch(pcap, 1, pcap_callback, (u_char *) p);
    }

    return 1;
}

int main (int argc, char **argv) {
    /* options */
    char c;
    int client_mode;

    /* Initialize default values */
    client_mode = 0;

    /* Retrieve command line options */
    while ((c = getopt(argc, argv, "hi:cm:")) != EOF) {
        switch(c) {
        case 'h':
            usage(argv[0]);
            return -1;
        case 'i':
            if (optarg) {
                ifname = optarg;
                break;
            } else {
                usage(argv[0]);
                return -1;
            }
        case 'c':
            client_mode = 1;
            break;
        case 'm':
            if (optarg) {
                magic_number = atoi(optarg);
                break;
            } else {
                usage(argv[0]);
                return -1;
            }
        default:
            usage(argv[0]);
            return -1;
        }
    }

    printf("\nRaw Covert (%s) proof-of-concept code\n"
           "Copyright (c) 2005-2006, %s\n\n"
           , version, author);

    if (ifname == NULL) {
        fprintf(stderr, "Error\t: Ifname must be specified\n");
        usage(argv[0]);
        return (-1);
    }

    /* Open raw socket */

    sock = open_sock (ifname, sock);

    if (sock == -1 ) {
        fprintf(stderr,
                "Error\t: Cannot open socket\n"
                "Info\t: Must be root with a 802.11 card with RFMON enabled\n");
        return (-1);
    }

    if (client_mode == 1)
        covert_client();
    else
        covert_server();

    fprintf(stderr, "Info\t: Terminated\n");

    return 1;
}

int usage (char *arg0) {
    printf("usage: %s [-h] [-i <ifname>] [-c] [-m <magic>]\n"
           "\t-h\t\t: display this message\n"
           "\t-i <ifname>\t: select interface for raw injection\n"
           "\t-c\t\t: client mode (default: server mode)\n"
           "\t-m <magic>\t: magic number\n"
           , arg0);
    return -1;
}
