/* * 802.11 to Ethernet pcap translator * * Copyright (C) 2006, 2007, 2008, 2009 Thomas d'Otreppe * Copyright (C) 2004, 2005 Christophe Devine * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. * If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. * If you * do not wish to do so, delete this exception statement from your * version. * If you delete this exception statement from all source * files in the program, then also delete it here. */ #include #include #include #include #include #include #include "version.h" #include "crypto.h" #include "pcap.h" #include "osdep/byteorder.h" #include "common.h" #define CRYPT_NONE 0 #define CRYPT_WEP 1 #define CRYPT_WPA 2 extern char * getVersion(char * progname, int maj, int min, int submin, int svnrev, int beta, int rc); extern int check_crc_buf( unsigned char *buf, int len ); extern int calc_crc_buf( unsigned char *buf, int len ); char usage[] = "\n" " %s - (C) 2006, 2007, 2008, 2009 Thomas d\'Otreppe\n" " Original work: Christophe Devine\n" " http://www.aircrack-ng.org\n" "\n" " usage: airdecap-ng [options] \n" "\n" " Common options:\n" " -l : don't remove the 802.11 header\n" " -b : access point MAC address filter\n" " -e : target network SSID\n" "\n" " WEP specific option:\n" " -w : target network WEP key in hex\n" "\n" " WPA specific options:\n" " -p : target network WPA passphrase\n" " -k : WPA Pairwise Master Key in hex\n" "\n" " --help : Displays this usage screen\n" "\n"; struct decap_stats { unsigned long nb_read; /* # of packets read */ unsigned long nb_wep; /* # of WEP data packets */ unsigned long nb_bad; /* # of bad data packets */ unsigned long nb_wpa; /* # of WPA data packets */ unsigned long nb_plain; /* # of plaintext packets */ unsigned long nb_unwep; /* # of decrypted WEP pkt */ unsigned long nb_unwpa; /* # of decrypted WPA pkt */ } stats; struct options { int no_convert; char essid[36]; char passphrase[65]; uchar bssid[6]; uchar pmk[40]; uchar wepkey[64]; int weplen, crypt; int store_bad; } opt; uchar buffer[65536]; uchar buffer2[65536]; /* this routine handles to 802.11 to Ethernet translation */ int write_packet( FILE *f_out, struct pcap_pkthdr *pkh, uchar *h80211 ) { int n; uchar arphdr[12]; int qosh_offset = 0; if( opt.no_convert ) { if( buffer != h80211 ) memcpy( buffer, h80211, pkh->caplen ); } else { /* create the Ethernet link layer (MAC dst+src) */ switch( h80211[1] & 3 ) { case 0: /* To DS = 0, From DS = 0: DA, SA, BSSID */ memcpy( arphdr + 0, h80211 + 4, 6 ); memcpy( arphdr + 6, h80211 + 10, 6 ); break; case 1: /* To DS = 1, From DS = 0: BSSID, SA, DA */ memcpy( arphdr + 0, h80211 + 16, 6 ); memcpy( arphdr + 6, h80211 + 10, 6 ); break; case 2: /* To DS = 0, From DS = 1: DA, BSSID, SA */ memcpy( arphdr + 0, h80211 + 4, 6 ); memcpy( arphdr + 6, h80211 + 16, 6 ); break; default: /* To DS = 1, From DS = 1: RA, TA, DA, SA */ memcpy( arphdr + 0, h80211 + 16, 6 ); memcpy( arphdr + 6, h80211 + 24, 6 ); break; } /* check QoS header */ if ( GET_SUBTYPE(h80211[0]) == IEEE80211_FC0_SUBTYPE_QOS ) { qosh_offset += 2; } /* remove the 802.11 + LLC header */ if( ( h80211[1] & 3 ) != 3 ) { pkh->len -= 24 + qosh_offset + 6; pkh->caplen -= 24 + qosh_offset + 6; memcpy( buffer + 12, h80211 + qosh_offset + 30, pkh->caplen ); } else { pkh->len -= 30 + qosh_offset + 6; pkh->caplen -= 30 + qosh_offset + 6; memcpy( buffer + 12, h80211 + qosh_offset + 36, pkh->caplen ); } memcpy( buffer, arphdr, 12 ); pkh->len += 12; pkh->caplen += 12; } n = sizeof( struct pcap_pkthdr ); if( fwrite( pkh, 1, n, f_out ) != (size_t) n ) { perror( "fwrite(packet header) failed" ); return( 1 ); } n = pkh->caplen; if( fwrite( buffer, 1, n, f_out ) != (size_t) n ) { perror( "fwrite(packet data) failed" ); return( 1 ); } return( 0 ); } int main( int argc, char *argv[] ) { time_t tt; uint magic; char *s, buf[128]; FILE *f_in, *f_out, *f_bad=NULL; unsigned long crc; int i = 0, n, z, linktype; uchar ZERO[32], *h80211; uchar bssid[6], stmac[6]; struct WPA_ST_info *st_1st; struct WPA_ST_info *st_cur; struct WPA_ST_info *st_prv; struct pcap_file_header pfh; struct pcap_pkthdr pkh; /* parse the arguments */ memset( ZERO, 0, sizeof( ZERO ) ); memset( &opt, 0, sizeof( opt ) ); while( 1 ) { int option_index = 0; static struct option long_options[] = { {"bssid", 1, 0, 'b'}, {"debug", 1, 0, 'd'}, {"help", 0, 0, 'H'}, {0, 0, 0, 0 } }; int option = getopt_long( argc, argv, "lb:k:e:p:w:H", long_options, &option_index ); if( option < 0 ) break; switch( option ) { case ':' : printf("\"%s --help\" for help.\n", argv[0]); return( 1 ); case '?' : printf("\"%s --help\" for help.\n", argv[0]); return( 1 ); case 'l' : opt.no_convert = 1; break; case 'b' : i = 0; s = optarg; while( sscanf( s, "%x", &n ) == 1 ) { if( n < 0 || n > 255 ) { printf( "Invalid BSSID (not a MAC).\n" ); printf("\"%s --help\" for help.\n", argv[0]); return( 1 ); } opt.bssid[i] = n; if( ++i >= 6 ) break; if( ! ( s = strchr( s, ':' ) ) ) break; s++; } if( i != 6 ) { printf( "Invalid BSSID (not a MAC).\n" ); printf("\"%s --help\" for help.\n", argv[0]); return( 1 ); } break; case 'k' : if( opt.crypt != CRYPT_NONE ) { printf( "Encryption key already specified.\n" ); printf("\"%s --help\" for help.\n", argv[0]); return( 1 ); } opt.crypt = CRYPT_WPA; i = 0; s = optarg; buf[0] = s[0]; buf[1] = s[1]; buf[2] = '\0'; while( sscanf( buf, "%x", &n ) == 1 ) { if( n < 0 || n > 255 ) { printf( "Invalid WPA PMK.\n" ); printf("\"%s --help\" for help.\n", argv[0]); return( 1 ); } opt.pmk[i++] = n; if( i >= 32 ) break; s += 2; if( s[0] == ':' || s[0] == '-' ) s++; if( s[0] == '\0' || s[1] == '\0' ) break; buf[0] = s[0]; buf[1] = s[1]; } if( i != 32 ) { printf( "Invalid WPA PMK.\n" ); printf("\"%s --help\" for help.\n", argv[0]); return( 1 ); } break; case 'e' : if ( opt.essid[0]) { printf( "ESSID already specified.\n" ); printf("\"%s --help\" for help.\n", argv[0]); return( 1 ); } memset( opt.essid, 0, sizeof( opt.essid ) ); strncpy( opt.essid, optarg, sizeof( opt.essid ) - 1 ); break; case 'p' : if( opt.crypt != CRYPT_NONE ) { printf( "Encryption key already specified.\n" ); printf("\"%s --help\" for help.\n", argv[0]); return( 1 ); } opt.crypt = CRYPT_WPA; memset( opt.passphrase, 0, sizeof( opt.passphrase ) ); strncpy( opt.passphrase, optarg, sizeof( opt.passphrase ) - 1 ); break; case 'w' : if( opt.crypt != CRYPT_NONE ) { printf( "Encryption key already specified.\n" ); printf("\"%s --help\" for help.\n", argv[0]); return( 1 ); } opt.crypt = CRYPT_WEP; i = 0; s = optarg; buf[0] = s[0]; buf[1] = s[1]; buf[2] = '\0'; while( sscanf( buf, "%x", &n ) == 1 ) { if( n < 0 || n > 255 ) { printf( "Invalid WEP key.\n" ); printf("\"%s --help\" for help.\n", argv[0]); return( 1 ); } opt.wepkey[i++] = n; if( i >= 64 ) break; s += 2; if( s[0] == ':' || s[0] == '-' ) s++; if( s[0] == '\0' || s[1] == '\0' ) break; buf[0] = s[0]; buf[1] = s[1]; } if( i != 5 && i != 13 && i != 16 && i != 29 && i != 61 ) { printf( "Invalid WEP key length. [5,13,16,29,61]\n" ); printf("\"%s --help\" for help.\n", argv[0]); return( 1 ); } opt.weplen = i; break; case 'H' : printf( usage, getVersion("Airdecap-ng", _MAJ, _MIN, _SUB_MIN, _REVISION, _BETA, _RC)); return( 1 ); default : goto usage; } } if( argc - optind != 1 ) { if(argc == 1) { usage: printf( usage, getVersion("Airdecap-ng", _MAJ, _MIN, _SUB_MIN, _REVISION, _BETA, _RC)); } if( argc - optind == 0) { printf("No file to decrypt specified.\n"); } if(argc > 1) { printf("\"%s --help\" for help.\n", argv[0]); } return( 1 ); } if( opt.crypt == CRYPT_WPA ) { if( opt.passphrase[0] != '\0' ) { /* compute the Pairwise Master Key */ if( opt.essid[0] == '\0' ) { printf( "You must also specify the ESSID (-e).\n" ); printf("\"%s --help\" for help.\n", argv[0]); return( 1 ); } calc_pmk( opt.passphrase, opt.essid, opt.pmk ); } } /* open the input and output pcap files */ if( ( f_in = fopen( argv[optind], "rb" ) ) == NULL ) { perror( "fopen failed\n" ); printf( "Could not open \"%s\".\n", argv[optind] ); return( 1 ); } n = sizeof( pfh ); if( fread( &pfh, 1, n, f_in ) != (size_t) n ) { perror( "fread(pcap file header) failed" ); return( 1 ); } if( pfh.magic != TCPDUMP_MAGIC && pfh.magic != TCPDUMP_CIGAM ) { printf( "\"%s\" isn't a pcap file (expected " "TCPDUMP_MAGIC).\n", argv[optind] ); return( 1 ); } if( ( magic = pfh.magic ) == TCPDUMP_CIGAM ) SWAP32( pfh.linktype ); if( pfh.linktype != LINKTYPE_IEEE802_11 && pfh.linktype != LINKTYPE_PRISM_HEADER && pfh.linktype != LINKTYPE_RADIOTAP_HDR && pfh.linktype != LINKTYPE_PPI_HDR ) { printf( "\"%s\" isn't a regular 802.11 " "(wireless) capture.\n", argv[optind] ); return( 1 ); } linktype = pfh.linktype; n = strlen( argv[optind] ); if( n > 4 && ( n + 5 < (int) sizeof( buffer ) ) && argv[optind][n - 4] == '.' ) { memcpy( buffer , argv[optind], n - 4 ); memcpy( buffer2, argv[optind], n - 4 ); memcpy( buffer + n - 4, "-dec", 4 ); memcpy( buffer2 + n - 4, "-bad", 4 ); memcpy( buffer + n, argv[optind] + n - 4, 5 ); memcpy( buffer2 + n, argv[optind] + n - 4, 5 ); } else { if( n > 5 && ( n + 6 < (int) sizeof( buffer ) ) && argv[optind][n - 5] == '.' ) { memcpy( buffer , argv[optind], n - 5 ); memcpy( buffer2, argv[optind], n - 5 ); memcpy( buffer + n - 5, "-dec", 4 ); memcpy( buffer2 + n - 5, "-bad", 4 ); memcpy( buffer + n - 1, argv[optind] + n - 5, 6 ); memcpy( buffer2 + n - 1, argv[optind] + n - 5, 6 ); } else { memset( buffer , 0, sizeof( buffer ) ); memset( buffer2, 0, sizeof( buffer ) ); snprintf( (char *) buffer , sizeof( buffer ) - 1, "%s-dec", argv[optind] ); snprintf( (char *) buffer2, sizeof( buffer ) - 1, "%s-bad", argv[optind] ); } } if( opt.crypt == CRYPT_WEP && opt.no_convert == 1 ) { opt.store_bad=1; } if( ( f_out = fopen( (char *) buffer, "wb+" ) ) == NULL ) { perror( "fopen failed" ); printf( "Could not create \"%s\".\n", buffer ); return( 1 ); } if(opt.store_bad) { if( ( f_bad = fopen( (char *) buffer2, "wb+" ) ) == NULL ) { perror( "fopen failed" ); printf( "Could not create \"%s\".\n", buffer2 ); return( 1 ); } } pfh.magic = TCPDUMP_MAGIC; pfh.version_major = PCAP_VERSION_MAJOR; pfh.version_minor = PCAP_VERSION_MINOR; pfh.thiszone = 0; pfh.sigfigs = 0; pfh.snaplen = 65535; pfh.linktype = ( opt.no_convert ) ? LINKTYPE_IEEE802_11 : LINKTYPE_ETHERNET; n = sizeof( pfh ); if( fwrite( &pfh, 1, n, f_out ) != (size_t) n ) { perror( "fwrite(pcap file header) failed" ); return( 1 ); } if(opt.store_bad) { if( fwrite( &pfh, 1, n, f_bad ) != (size_t) n ) { perror( "fwrite(pcap file header) failed" ); return( 1 ); } } /* loop reading and deciphering the packets */ memset( &stats, 0, sizeof( stats ) ); tt = time( NULL ); st_1st = NULL; while( 1 ) { if( time( NULL ) - tt > 0 ) { /* update the status line every second */ printf( "\33[KRead %ld packets...\r", stats.nb_read ); fflush( stdout ); tt = time( NULL ); } /* read one packet */ n = sizeof( pkh ); if( fread( &pkh, 1, n, f_in ) != (size_t) n ) break; if( magic == TCPDUMP_CIGAM ) SWAP32( pkh.caplen ); n = pkh.caplen; if( n <= 0 || n > 65535 ) { printf( "Corrupted file? Invalid packet length %d.\n", n ); break; } if( fread( buffer, 1, n, f_in ) != (size_t) n ) break; stats.nb_read++; h80211 = buffer; if( linktype == LINKTYPE_PRISM_HEADER ) { /* remove the prism header */ if( h80211[7] == 0x40 ) n = 64; /* prism54 */ else { n = *(int *)( h80211 + 4 ); if( magic == TCPDUMP_CIGAM ) SWAP32( n ); } if( n < 8 || n >= (int) pkh.caplen ) continue; h80211 += n; pkh.caplen -= n; } if( linktype == LINKTYPE_RADIOTAP_HDR ) { /* remove the radiotap header */ n = *(unsigned short *)( h80211 + 2 ); if( n <= 0 || n >= (int) pkh.caplen ) continue; h80211 += n; pkh.caplen -= n; } if( linktype == LINKTYPE_PPI_HDR ) { /* Remove the PPI header */ n = le16_to_cpu(*(unsigned short *)( h80211 + 2)); if( n <= 0 || n>= (int) pkh.caplen ) continue; /* for a while Kismet logged broken PPI headers */ if ( n == 24 && le16_to_cpu(*(unsigned short *)(h80211 + 8)) == 2 ) n = 32; if( n <= 0 || n>= (int) pkh.caplen ) continue; h80211 += n; pkh.caplen -= n; } /* remove the FCS if present (madwifi) */ if( check_crc_buf( h80211, pkh.caplen - 4 ) == 1 ) { pkh.len -= 4; pkh.caplen -= 4; } /* check if data */ if( ( h80211[0] & 0x0C ) != 0x08 ) continue; /* check minimum size */ z = ( ( h80211[1] & 3 ) != 3 ) ? 24 : 30; if( z + 16 > (int) pkh.caplen ) continue; /* check QoS header */ if ( GET_SUBTYPE(h80211[0]) == IEEE80211_FC0_SUBTYPE_QOS ) { z += 2; } /* check the BSSID */ switch( h80211[1] & 3 ) { case 0: memcpy( bssid, h80211 + 16, 6 ); break; //Adhoc case 1: memcpy( bssid, h80211 + 4, 6 ); break; //ToDS case 2: memcpy( bssid, h80211 + 10, 6 ); break; //FromDS case 3: memcpy( bssid, h80211 + 10, 6 ); break; //WDS -> Transmitter taken as BSSID } if( memcmp( opt.bssid, ZERO, 6 ) != 0 ) if( memcmp( opt.bssid, bssid, 6 ) != 0 ) continue; /* locate the station's MAC address */ switch( h80211[1] & 3 ) { case 1: memcpy( stmac, h80211 + 10, 6 ); break; case 2: memcpy( stmac, h80211 + 4, 6 ); break; case 3: memcpy( stmac, h80211 + 10, 6 ); break; default: continue; } st_prv = NULL; st_cur = st_1st; while( st_cur != NULL ) { if( ! memcmp( st_cur->stmac, stmac, 6 ) ) break; st_prv = st_cur; st_cur = st_cur->next; } /* if it's a new station, add it */ if( st_cur == NULL ) { if( ! ( st_cur = (struct WPA_ST_info *) malloc( sizeof( struct WPA_ST_info ) ) ) ) { perror( "malloc failed" ); break; } memset( st_cur, 0, sizeof( struct WPA_ST_info ) ); if( st_1st == NULL ) st_1st = st_cur; else st_prv->next = st_cur; memcpy( st_cur->stmac, stmac, 6 ); memcpy( st_cur->bssid, bssid, 6 ); } /* check if we haven't already processed this packet */ crc = calc_crc_buf( h80211 + z, pkh.caplen - z ); if( ( h80211[1] & 3 ) == 2 ) { if( st_cur->t_crc == crc ) continue; st_cur->t_crc = crc; } else { if( st_cur->f_crc == crc ) continue; st_cur->f_crc = crc; } /* check the SNAP header to see if data is encrypted * * as unencrypted data begins with AA AA 03 00 00 00 */ if( h80211[z] != h80211[z + 1] || h80211[z + 2] != 0x03 ) { /* check the extended IV flag */ if( ( h80211[z + 3] & 0x20 ) == 0 ) { uchar K[64]; stats.nb_wep++; if( opt.crypt != CRYPT_WEP ) continue; memcpy( K, h80211 + z, 3 ); memcpy( K + 3, opt.wepkey, opt.weplen ); if(opt.store_bad) memcpy(buffer2, h80211, pkh.caplen); if( decrypt_wep( h80211 + z + 4, pkh.caplen - z - 4, K, 3 + opt.weplen ) == 0 ) { if(opt.store_bad) { stats.nb_bad++; memcpy(h80211, buffer2, pkh.caplen); if( write_packet( f_bad, &pkh, h80211 ) != 0 ) break; } continue; } /* WEP data packet was successfully decrypted, * * remove the WEP IV & ICV and write the data */ pkh.len -= 8; pkh.caplen -= 8; memcpy( h80211 + z, h80211 + z + 4, pkh.caplen - z ); stats.nb_unwep++; h80211[1] &= 0xBF; if( write_packet( f_out, &pkh, h80211 ) != 0 ) break; } else { stats.nb_wpa++; if( opt.crypt != CRYPT_WPA ) continue; /* if the PTK is valid, try to decrypt */ if( st_cur == NULL || ! st_cur->valid_ptk ) continue; if( st_cur->keyver == 1 ) { if( decrypt_tkip( h80211, pkh.caplen, st_cur->ptk + 32 ) == 0 ) continue; pkh.len -= 20; pkh.caplen -= 20; } else { if( decrypt_ccmp( h80211, pkh.caplen, st_cur->ptk + 32 ) == 0 ) continue; pkh.len -= 16; pkh.caplen -= 16; } /* WPA data packet was successfully decrypted, * * remove the WPA Ext.IV & MIC, write the data */ memcpy( h80211 + z, h80211 + z + 8, pkh.caplen - z ); stats.nb_unwpa++; h80211[1] &= 0xBF; if( write_packet( f_out, &pkh, h80211 ) != 0 ) break; } } else { /* check ethertype == EAPOL */ z += 6; if( h80211[z] != 0x88 || h80211[z + 1] != 0x8E ) { stats.nb_plain++; if( opt.crypt != CRYPT_NONE ) continue; if( write_packet( f_out, &pkh, h80211 ) != 0 ) break; else continue; } z += 2; /* type == 3 (key), desc. == 254 (WPA) or 2 (RSN) */ if( h80211[z + 1] != 0x03 || ( h80211[z + 4] != 0xFE && h80211[z + 4] != 0x02 ) ) continue; /* frame 1: Pairwise == 1, Install == 0, Ack == 1, MIC == 0 */ if( ( h80211[z + 6] & 0x08 ) != 0 && ( h80211[z + 6] & 0x40 ) == 0 && ( h80211[z + 6] & 0x80 ) != 0 && ( h80211[z + 5] & 0x01 ) == 0 ) { /* set authenticator nonce */ memcpy( st_cur->anonce, &h80211[z + 17], 32 ); } /* frame 2 or 4: Pairwise == 1, Install == 0, Ack == 0, MIC == 1 */ if( ( h80211[z + 6] & 0x08 ) != 0 && ( h80211[z + 6] & 0x40 ) == 0 && ( h80211[z + 6] & 0x80 ) == 0 && ( h80211[z + 5] & 0x01 ) != 0 ) { if( memcmp( &h80211[z + 17], ZERO, 32 ) != 0 ) { /* set supplicant nonce */ memcpy( st_cur->snonce, &h80211[z + 17], 32 ); } /* copy the MIC & eapol frame */ st_cur->eapol_size = ( h80211[z + 2] << 8 ) + h80211[z + 3] + 4; memcpy( st_cur->keymic, &h80211[z + 81], 16 ); memcpy( st_cur->eapol, &h80211[z], st_cur->eapol_size ); memset( st_cur->eapol + 81, 0, 16 ); /* copy the key descriptor version */ st_cur->keyver = h80211[z + 6] & 7; } /* frame 3: Pairwise == 1, Install == 1, Ack == 1, MIC == 1 */ if( ( h80211[z + 6] & 0x08 ) != 0 && ( h80211[z + 6] & 0x40 ) != 0 && ( h80211[z + 6] & 0x80 ) != 0 && ( h80211[z + 5] & 0x01 ) != 0 ) { if( memcmp( &h80211[z + 17], ZERO, 32 ) != 0 ) { /* set authenticator nonce */ memcpy( st_cur->anonce, &h80211[z + 17], 32 ); } /* copy the MIC & eapol frame */ st_cur->eapol_size = ( h80211[z + 2] << 8 ) + h80211[z + 3] + 4; memcpy( st_cur->keymic, &h80211[z + 81], 16 ); memcpy( st_cur->eapol, &h80211[z], st_cur->eapol_size ); memset( st_cur->eapol + 81, 0, 16 ); /* copy the key descriptor version */ st_cur->keyver = h80211[z + 6] & 7; } st_cur->valid_ptk = calc_ptk( st_cur, opt.pmk ); } } fclose( f_in ); fclose( f_out ); if(opt.store_bad) fclose( f_bad ); /* write some statistics */ printf( "\33[KTotal number of packets read % 8ld\n" "Total number of WEP data packets % 8ld\n" "Total number of WPA data packets % 8ld\n" "Number of plaintext data packets % 8ld\n" "Number of decrypted WEP packets % 8ld\n" "Number of corrupted WEP packets % 8ld\n" "Number of decrypted WPA packets % 8ld\n", stats.nb_read, stats.nb_wep, stats.nb_wpa, stats.nb_plain, stats.nb_unwep, stats.nb_bad, stats.nb_unwpa ); return( 0 ); }