/* Ultra-fast dhcp discovery * * $Id: dhcp_discover.c,v 2004/01/03 20:31:01 mike Exp $ * dhd.c,v LatinSuD 2006 * * Compile: * gcc -o dhd dhd.c -lnet -lpcap -lpthread * * Example usage: * dhd * dhd eth0 * * Example output: * #0: * SERVER-MAC: 00:01:38:12:34:56 * IP: 192.168.1.1 * CLIENT-MAC: 00:0e:35:ab:cd:ef * IP: 192.168.1.40 * * >> "0 "0 * * Dhd injects 10 dhcp requests on interface. In this case, * it has been received a response, numbered as "0". * Later, response 0 was received twice more. */ #define DHD_VERSION "2.1" /* * Changes: * 2.1 (2007-04-07): * - Identify equivalent packets with different time fields * - Add domain-suffix field * - Minor cleanup & enhanced usage help * * 2.0 (2006-??-??): * - Beautify printf * * libnet 1.1 * Build a DHCP discover packet * To view: /usr/sbin/tcpdump -vvvvven -s 4096 'port 67 or port 68' * * Copyright (c) 1998 - 2004 Mike D. Schiffman * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include #include #include #include #include #include #include #include /* Ethernet addresses are 6 bytes */ #ifndef ETHER_ADDR_LEN #define ETHER_ADDR_LEN 6 #endif /* Ethernet header */ struct sniff_ethernet { u_char ether_dhost[ETHER_ADDR_LEN]; /* Destination host address */ u_char ether_shost[ETHER_ADDR_LEN]; /* Source host address */ u_short ether_type; /* IP? ARP? RARP? etc */ }; /* IP header */ struct sniff_ip { u_char ip_vhl; /* version << 4 | header length >> 2 */ u_char ip_tos; /* type of service */ u_short ip_len; /* total length */ u_short ip_id; /* identification */ u_short ip_off; /* fragment offset field */ u_char ip_ttl; /* time to live */ u_char ip_p; /* protocol */ u_short ip_sum; /* checksum */ struct in_addr ip_src, ip_dst; /* source and dest address */ }; typedef struct { u_short uh_sport; /* source port */ u_short uh_dport; /* destination port */ u_short len; u_short chk; unsigned char type; char a; char b; char c; u_short d; u_short e; } sniff_udp; /* ethernet headers are always exactly 14 bytes */ int SIZE_ETHERNET = 14; #define IP_HL(ip) (((ip)->ip_vhl) & 0x0f) #define IP_V(ip) (((ip)->ip_vhl) >> 4) const struct sniff_ethernet *ethernet; /* The ethernet header */ struct sniff_ip *ip; /* The IP header */ const struct sniff_tcp *tcp; /* The TCP header */ const char *payload; /* Packet payload */ pthread_t sniffer, injector; void print_mac(unsigned char *packet, int moffset) { int i; if (moffset > 80) exit(0); for (i = 0; i < 6; i++) printf("%02x%c", packet[i + moffset], (i < 5) ? ':' : '\0'); } void print_ip(unsigned char *ip, int offset) { int i; for (i = 0; i < 4; i++) { printf("%d", ip[i + offset]); if (i < 3) printf("."); } } #define DNS 0 #define MASK 1 #define CIP 2 #define SIP 3 #define GW 4 int didCR = 1; #define HASH 8 #define PCACHE 256 int h_pack[PCACHE]; int n_pack = 0; unsigned int miChecksum(u_char * packet, int len) { int i; int s1 = 0; int s2 = 0; u_char * ptr=packet; while (ptr - packet < len - 1) { if (ptr - packet + ptr[1] + 2 > len) break; // ignore time dependent fields if (ptr[0]==2 || ptr[0]==51 || ptr[0]==58 || ptr[0]==59) { ptr += 2 + ptr[1]; continue; } for (i = 0; i < ptr[1]+2; i++) { s1 += ptr[i] * i; s2 += ptr[i]; } ptr += 2 + ptr[1]; } return s1 + (s2 << 16); } // Caches known packets, so they are printed only once int cache_packet(u_char * packet, int len, int *num) { int i; int hash = miChecksum(packet, len); for (i = 0; i < n_pack; i++) { if (h_pack[i] == hash) { *num = i; return 0; } } if (n_pack < PCACHE) { h_pack[n_pack] = hash; *num = n_pack; n_pack++; } return -1; } // Pcap callback that does all the work void got_packet(u_char * args, const struct pcap_pkthdr *header, const u_char * packet) { sniff_udp *udp; int i; uint size_ip; u_char *ptr; int idx; int dijoIp = 0; ethernet = (struct sniff_ethernet *) (packet); ip = (struct sniff_ip *) (packet + SIZE_ETHERNET); size_ip = (IP_HL(ip)) * 4; udp = (sniff_udp *) (packet + SIZE_ETHERNET + size_ip); if (*(((char *) udp) + 8) != 2) return; ptr = ((unsigned char *) udp) + 0xf8; if (cache_packet(ptr, header->len - (ptr - packet), &idx) == 0) { // packet already in cache, show only reference number if (didCR) printf("\n >>"); printf(" \"%d ", idx); didCR = 0; return; } else { // start dissecting packet puts(""); if (!didCR) puts(""); printf(" #%d:\n", idx); } printf("\tSERVER-MAC: \x1b[1m"); print_mac((unsigned char *) packet, 6); printf("\x1b[0m\n"); // print only known interesting fields while (ptr - packet < header->len - 1) { if (ptr - packet + ptr[1] + 2 > header->len) break; if (ptr[0] == 6) { printf("\tDNS: \x1b[1m"); for (i = 0; i < (ptr[1] / 4); i++) { if (i != 0) printf(", "); print_ip(ptr, 2 + 4 * i); } printf("\x1b[0m\n"); } else if (ptr[0] == 1) { printf("\t MASK: \x1b[1m"); print_ip(ptr, 2); printf("\x1b[0m\n"); } else if (ptr[0] == 54) { printf("\t IP: \x1b[1m"); print_ip(ptr, 2); printf("\x1b[0m\n"); printf("\tCLIENT-MAC: \x1b[1m"); print_mac((unsigned char *) udp, 0x24); printf("\x1b[0m\n"); printf("\t IP: \x1b[1m"); print_ip((unsigned char *) udp, 0x18); printf("\x1b[0m\n"); dijoIp = 1; } else if (ptr[0] == 15) { printf("\tDOMAIN-SUFFIX: \x1b[1m"); for (i=0; i=0x20 && ptr[2+i]<0x7F)?ptr[2+i]:' '); printf("\x1b[0m\n"); } else if (ptr[0] == 3) { printf("\tGW: \x1b[1m"); print_ip(ptr, 2); printf("\x1b[0m\n"); } else if (ptr[0] == 53) { if (ptr[2] != 2) break; } ptr += 2 + ptr[1]; } if (!dijoIp) { printf("\tCLIENT-IP: \x1b[1m"); print_ip((unsigned char *) udp, 0x18); printf("\x1b[0m\n"); } didCR = 1; } void usage(char *prog) { fprintf(stderr, "DHD " DHD_VERSION " - A fast DHcp Discover tool.\nUsage: dhd [INTERFACE]\n\tIf no INTERFACE is given, it will write on all available interfaces and sniff on any.\n"); exit(1); } typedef struct { char *intf; libnet_t *l; pcap_t *pcap; struct libnet_ether_addr *ethaddr; } context; struct libnet_stats ls; u_char *options; void init_libnet(context * c) { char errbuf[LIBNET_ERRBUF_SIZE]; int i; c->l = libnet_init(LIBNET_LINK, // injection type c->intf, // network interface errbuf); // errbuf if (!c->l) { fprintf(stderr, "libnet_init: %s", errbuf); exit(EXIT_FAILURE); } if ((c->ethaddr = libnet_get_hwaddr(c->l)) == NULL) { fprintf(stderr, "libnet_get_hwaddr: %s\n", libnet_geterror(c->l)); exit(EXIT_FAILURE); } for (i = 0; i < 6; i++) { fprintf(stderr, "%02x", c->ethaddr->ether_addr_octet[i]); if (i != 5) { fprintf(stderr, ":"); } } } void init_packet(context * c) { u_long options_len, orig_len; int i; libnet_ptag_t t; libnet_ptag_t ip; libnet_ptag_t udp; libnet_ptag_t dhcp; u_char options_req[] = { LIBNET_DHCP_SUBNETMASK, LIBNET_DHCP_BROADCASTADDR, LIBNET_DHCP_TIMEOFFSET, LIBNET_DHCP_ROUTER, LIBNET_DHCP_DOMAINNAME, LIBNET_DHCP_DNS, LIBNET_DHCP_HOSTNAME }; u_char enet_dst[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; u_char *tmp; // build options packet i = 0; options_len = 3; // update total payload size // we are a discover packet options = malloc(3); options[i++] = LIBNET_DHCP_MESSAGETYPE; // type options[i++] = 1; // len options[i++] = LIBNET_DHCP_MSGDISCOVER; // data orig_len = options_len; options_len += sizeof(options_req) + 2; // update total payload size // workaround for realloc on old machines // options = realloc(options, options_len); // resize options buffer tmp = malloc(options_len); memcpy(tmp, options, orig_len); free(options); options = tmp; // we are going to request some parameters options[i++] = LIBNET_DHCP_PARAMREQUEST; // type options[i++] = sizeof(options_req); // len memcpy(options + i, options_req, sizeof(options_req)); // data i += sizeof(options_req); // end our options packet // workaround for realloc on old machines // options = realloc(options, options_len); // resize options buffer orig_len = options_len; options_len += 1; tmp = malloc(options_len); memcpy(tmp, options, orig_len); free(options); options = tmp; options[i++] = LIBNET_DHCP_END; // make sure we are at least the minimum length, if not fill // this could go in libnet, but we will leave it in the app for now if (options_len + LIBNET_DHCPV4_H < LIBNET_BOOTP_MIN_LEN) { orig_len = options_len; options_len = LIBNET_BOOTP_MIN_LEN - LIBNET_DHCPV4_H; // workaround for realloc on old machines // options = realloc(options, options_len); tmp = malloc(options_len); memcpy(tmp, options, orig_len); free(options); options = tmp; memset(options + i, 0, options_len - i); } // the goodies are here dhcp = libnet_build_dhcpv4(LIBNET_DHCP_REQUEST, // opcode 1, // hardware type 6, // hardware address length 0, // hop count 0xdeadbeee, // transaction id 0, // seconds since bootstrap 0x8000, // flags 0, // client ip 0, // your ip 0, // server ip 0, // gateway ip c->ethaddr->ether_addr_octet, // client hardware addr //unamac, // client hardware addr NULL, // server host name NULL, // boot file options, // dhcp options stuck in payload since it is dynamic options_len, // length of options c->l, // libnet handle 0); // libnet id // wrap it udp = libnet_build_udp(68, // source port 67, // destination port LIBNET_UDP_H + LIBNET_DHCPV4_H + options_len, // packet size 0, // checksum NULL, // payload 0, // payload size c->l, // libnet handle 0); // libnet id // hook me up with some ipv4 ip = libnet_build_ipv4(LIBNET_IPV4_H + LIBNET_UDP_H + LIBNET_DHCPV4_H + options_len, // length 0x10, // TOS 0, // IP ID 0, // IP Frag 16, // TTL IPPROTO_UDP, // protocol 0, // checksum inet_addr("0.0.0.0"), // src ip inet_addr("255.255.255.255"), // destination ip NULL, // payload 0, // payload size c->l, // libnet handle 0); // libnet id // we can just autobuild since we arent doing anything tricky t = libnet_autobuild_ethernet(enet_dst, // ethernet destination ETHERTYPE_IP, // protocol type c->l); // libnet handle } int init_pcap(char *intf) { char errbuf[PCAP_ERRBUF_SIZE]; pcap_t *pcap; struct bpf_program fp; /* The compiled filter expression */ char filter_exp[] = "udp and (port 67 or port 68)"; /* The filter expression */ bpf_u_int32 net = 0; /* The IP of our sniffing device */ setbuf(stdout, 0); //pcap = pcap_open_offline("sample.cap", errbuf); pcap = pcap_open_live(intf, BUFSIZ, 0, 1000, errbuf); if (pcap == NULL) { fprintf(stderr, "Couldn't open device %s: %s\n", "any", errbuf); return (2); } if (pcap_compile(pcap, &fp, filter_exp, 0, net) == -1) { fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(pcap)); return (2); } if (pcap_setfilter(pcap, &fp) == -1) { fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(pcap)); return (2); } if (pcap_datalink(pcap) == DLT_LINUX_SLL) SIZE_ETHERNET = 16; //if (pcap_datalink (pcap) == DLT_EN10MB); pcap_loop(pcap, -1, got_packet, (unsigned char *) errbuf); return (0); } void inyecta(context * c) { // write to the wire if (libnet_write(c->l) == -1) { fprintf(stderr, " dhd: libnet_write: %s\n", strerror(errno)); } } void end_libnet(context * c) { libnet_destroy(c->l); // free mem free(options); exit(0); } int get_datalink(char *intf) { int res; pcap_t *handle; char buf[8192]; handle = pcap_open_live(intf, 0, 0, 0, buf); if (!handle) { //printf("NOP SE PUDO ABRIR [%s] : %s\n", intf, buf); return 0; } res = pcap_datalink(handle); //printf(" :%s: ", pcap_datalink_val_to_description(res)); pcap_close(handle); return res; } context ifaces[16]; int nIfaces = 0; void init_iface(char * iface) { if (get_datalink(iface) == DLT_EN10MB) { ifaces[nIfaces].intf = strdup(iface); fprintf(stderr, "Injecting on %s (", ifaces[nIfaces].intf); init_libnet(&ifaces[nIfaces]); fprintf(stderr, ")\n"); init_packet(&ifaces[nIfaces]); nIfaces++; } } void init_all_ifaces() { FILE *f; char *i1, *i2; char buf[1024]; f = fopen("/proc/net/dev", "r"); if (!f) return; while (fgets(buf, 1023, f) && nIfaces<16) { i1 = buf; while (*i1 == ' ') i1++; i2 = i1; while (*i2 != '\0') { if (*i2 == ':') { *i2 = '\0'; if (strcmp(i1, "lo")) { init_iface(i1); } } i2++; } } } void *esnifa(void *data) { fprintf(stderr, "Sniffing on %s\n", (char *) data); init_pcap((char *) data); return NULL; } int main(int argc, char *argv[]) { int i, j, dormir; if (getuid() != 0) { fprintf(stderr, "Need to be root!\n"); return -1; } setbuf(stdout, 0); dormir = 100; // init if (argc == 1) { pthread_create(&sniffer, NULL, esnifa, "any"); init_all_ifaces(); } else if (argc == 2) { if (!strcmp(argv[1],"-h") || !strcmp(argv[1],"--help")) usage(argv[0]); pthread_create(&sniffer, NULL, esnifa, (void *) argv[1]); init_iface(argv[1]); } else { fprintf(stderr, "Only 1 interface can be specified!\n"); usage(argv[0]); } // inject all packets on all ifaces for (i = 0; i < 10; i++) { usleep(dormir * 1000); for (j = 0; j < nIfaces; j++) { inyecta(&ifaces[j]); } dormir = dormir + 100; } usleep(250000); if (!didCR) puts(""); for (j = 0; j < nIfaces; j++) end_libnet(&ifaces[j]); return 0; }