From: johan@blade.stack.urc.tue.nl (Johan Wevers)
Newsgroups: nlnet.misc
Subject: WP password finder
Date: 16 Dec 92 09:23:58 GMT
Distribution: nlnet
Organization: Department of Physics, Eindhoven University of Technology.

Ik heb dit een poosje geleden opgepikt van alt.security. Het gaat over
het vinden van de passwords van een gecodeerde WP 5.x file. Ik krijg
echter het eerste programma, wpcrack, niet gecompileerd. C++ blijft
bezig over teveel }, en als ik die weghaal zit er een undefined symbol
_main in een of andere .obj file. Ik heb contact opgenomen met de poster
maar die heeft niet meer gereageerd. Weet iemand hier mischien wat er
fout is? Mijn kennis van C is daar te beperkt voor.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  wpcrack.doc wpcrack.c
# Wrapped by shipley@dpb1 on Thu Sep 17 14:59:04 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'wpcrack.doc' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'wpcrack.doc'\"
else
echo shar: Extracting \"'wpcrack.doc'\" \(14549 characters\)
sed "s/^X//" >'wpcrack.doc' <<'END_OF_FILE'
XWPCRACK 1.0 - Word Perfect 5.x Password Finder
XNow, files with forgotten passwords are no longer lost forever!
X
XCopyright (C)1991 Ron Dippold -- All Rights Reserved Except Those Given
X
XYou may freely distribute these files to anyone or any BBS and use the programs
Xfor free as long as you don't modify or delete any of these files.  I'm not
Xresponsible for any damage you do with this (I really hate legalese!).
X
X
X
XWhat This Is
X------------------------------------------------------------------------------
XWord Perfect's manual claims that "You can protect or lock your documents with
Xa password so that no one will be able to retrieve or print the file without
Xknowing the password - not even you," and "If you forget the password, there
Xis absolutely no way to retrieve the document."  [1]
X
XPretty impressive!  Actually, you could crack the password of a Word Perfect
X5.x file on a 8 1/2" x 11" sheet of paper, it's so simple.  If you are counting
Xyour files being safe, they are NOT.  Bennet [2] originally discovered how the
Xfile was encrypted, and Bergen and Caelli [3] determined further information
Xregarding version 5.x.	I have taken these papers, extended them, and written
Xsome programs to extract the password from the file.
X
XIf you don't care about theory, skip to the next section (Why This Program).
X
X
XStupid, Stupid, Stupid!
X-----------------------------------------------------------------------------
XWord Perfect allows you to add a password to a file.  When you save it, it will
Xbe encrypted, and it requires the password to uncrypt it.  Word Perfect
Xencrypts the file by a method of two exclusive ORs.  First, it takes the length
Xof the password plus 1 and increments this for each character, wrapping from
X255 to 0.  This incrementing sequence is XORed with the text to be encrypted.
XSecondly, the password is repeatedly XORed with blocks of the plaintext of
Xthe same size as the password.
X
XAs a simple example, say the password is "BONE".  The incrementing sequence
Xwould start with 5 (one more than the number of characters in BONE).  BONE in
Xhexidecimal is 42 4F 4E 45.
X
X	Text:        H  e  l  l  o  !
X							 48 65 6C 6C 6F 21
X	Sequence:    05 06 07 08 09 0A
X	Password:    42 4F 4E 45 42 4F
X	-------------------------------
X	Result:      0F 2C 25 21 24 64
X
XIt also stores a 16-bit checksum that is computed as following:
X
X	checksum = 0
X	for each character in the password {
X		rotate the checksum right by one bit
X		XOR the character into the high 8 bits of the checksum
X	}
X
XNow, this method of encryption worked well in Word Perfect 4.x., where the
Xactual text is the only thing encrypted.  To determine the password that was
Xused, you would have to determine the characters and the length.  Not easy, and
Xit would take lots of compuuting power, basically brute force trying different
Xpasswords that had the correct checksums (very thoughtful of them to leave that
Xin there).
X
XHowever, someone did a very dumb thing and extended this scheme to Word Perfect
X5.x incorrectly.  In Word Perfect 5.x, when you encrypt a file IT ENCRYPTS
XINFORMATION THAT IS CONSTANT FROM FILE TO FILE.  There are certain bytes that
Xare guaranteed to be the same always, and 22 total bytes that never seem to
Xchange.
X
XWhat this means is that we have known plaintext for some encrypted text!  And
Xbecause the method of encryption is so regular, it is a simple method to
Xreverse the encryption method to find the password.  The password is repeatedly
Xapplied to the text, so we have a check.
X
XSay we are trying a password length of 4.  We know the incrementing sequence
Xmust start at 5 (4+1).  So we XOR that, the first encrypted byte, and the known
Xvalue of that byte and we get back a character.  This might be the first
Xcharacter of the password.  Now, we check it!  We just move over 4 bytes (since
Xit repeats), increment the sequence number by 4, and repeat.  If the character
Xis the same as the password we first got, this could be the password.
X
XIn reality, it's not so easy.  The known bytes are spread out, and for some
Xpassword lengths there are some letters we can't figure out.  And if anything
Xthat I expect to stay constant changes that'll mess things up.
X
XThe first problem can be solved if there is only one missing character: since
XWP was so nice as to include the checksum, I can just try all possible values
Xof the missing character until the checksum matches.
X
XThe second problem I deal with by trying to get up to five complete versions
Xof the password for each length from different sections of the known info.
XThen I run a "vote" to see how well each charcter agrees with each other and
Xan overall confidence level for that length.  Any that are above a threshold
XI explore further as shown below.
X
XI also know that certain password values are forbidden, namely anything above
X128 and lower case characters (they are translated to upper case) so this helps
Xpare down the choices.
X
XIf there is more than one missing character (possible on very long passwords,
Xabove 14 characters), then it is up to the person running the program to figure
Xout what they could possibly be.  After all, if it is "IMP_RTANT REPO_T" where
Xthe "_" are the missing characters, "IMPORTANT REPORT" should be obvious to
Xmost people.
X
XThe method is very flexible, it seems to get most passwords easily, and
Xanything really strange should be caught by the flexible threshold.
X
X
X
XWhy This Program
X------------------------------------------------------------------------------
XI know people are going to instantly accuse me of writing this just as a tool
Xfor hackers to steal confidential data.  However, I have seen too many people
Xasking how to recover their own locked files ("I forgot my password!") and
Xlosing many hours to think that this does not have plenty of legitimate use.
X
XThe algorithm for cracking the programs has been available for quite some time,
XI just automated it and did some filling in the blanks.  Anyone who thought
Xtheir files were safe when encrypted like this was fooling themselves (or were
Xfooled by the claims made about the encryption).  What can I say, every tool
Xhas the potential for abuse.  The idea here is for you to save your files that
Xyou've forgotten the password to.
X
XWPCRACK also has some purely technical interest value.  Watching how easily
Xit can crack a WP file, even on a 4 MHz XT, can be unnerving.  The algorithm
Xreally doesn't take that much processing power.
X
X
X
XHow to Use It
X-------------------------------------------------------------------------------
X
XThe syntax is:
X   WPCRACK (-d) (-t) <filename> ( <threshold> )
X
XParameters:
X
X<filename> is the Word Perfect file to crack.
X
X<threshold> is the percentage confidence threshold over which a length is
Xconsidered to be a possibility.  This can vary from 0 (always) to 100
X(only the best) and defaults to 80.  If you can't get a password from
Xa file you KNOW is protected, lower the threshold.  You'll have more
Xgarbage to sort through, but you'll get something.
X
XSwitches:
X
X-d : Normally when WPCRACK prints the characters, it prints the actual
X     characters. Unfortunately, control characters are allowed in passwords,
X     and these can do strange things to the screen output.  The -d switch
X     will print the decimal values of each character on the line below.  This
X     is not pretty, but may be the only option you have to find out what
X     character that really is.
X
X-t : This will force WPCRACK to print all answers in table form, rather
X     that using special displays for passwords with one or no missing
X     characters (see below).  This lets you see ALL possible passwords.
X
XNote:
X
XWPCRACK can spit out quite a bit out output.  You might consider redirecting
Xthe output to a file by adding a "> filename" to the end of the line if it goes
Xby too fast.  This will send all the output to the file, which you can then
Xedit, print, or do whatever you want with.  For example:
X
X  WPCRACK MYFILE.WP > OUTPUT.TXT
X
XAnd then you can view or print OUTPUT.TXT
X
X
X
XThe Results
X-------------------------------------------------------------------------------
XWPCRACK will check the file for passwords of length 1 to 17 currently.  It will
Xgenerate up to five passwords for each length (less for the larger passwords,
Xas we only have 22 bytes to work with total).  It then decides how closely
Xall the guesses agree with each other.  If we chose the wrong length, we would
Xexpect the different guesses to be almost random.  If the length is correct,
Xthey should almost all agree.
X
XWPCRACK weights the results for each length to get a "confidence level" and
Xthen compares that to the threshold.  Any below the threshold are automatically
Xdiscarded and no longer figure in anything more.
X
XThe remaining lengths are sorted by confidence level, highest to lowest.  Then
Xfor each of the lengths, the following is done.
X
XCRACKWP checks to see how many missing letters there are.  These are positions
Xin the password that we have absolutely _no_ guesses for.
X
XNo Missing Characters:
X
XIf there are no missing characters, WPCRACK tried every combination of the
Xpossible characters for each position.  It determines the checksum for that
Xcombination, and if it matches the checksum stored in the file, it prints the
Xpassword.
X
XFor a file where the correct password is "PASS1" you will see:
X
X  PASS1   -- Good Checksum
X
X
XOne Missing Character:
X
XWhen there is only one missing character, WPCRACK makes use of the checksum
Xstored in the file.  It generates every combination of the known characters
Xin the password.  Then it works backwards from these and the checksum to
Xfigure out what the character must have been.  If the character is a legal
Xcharacter for a password, the resulting password is printed.
X
XFor a file where the correct password is "BIG_PASSWORD" you might see:
X
X
X  BIG_PASSWO D
X            ^ 
X The missing character will be extrapolated from the checksum
X
X  BIG_PASSWORD   -- Good Checksum
X
X
XMore Missing Characters or Table Mode:
X
XWhen there is more that one missing character, there isn't anything to do
Xexcept show you all the possibilities and let the superior text recognition
Xsystem in your head see if it recognizes anything reasonable.
X
XIn addition, if just letting WPCRYPT run doesn't produce the desired results,
Xthis will let you bypass all the extra processing and do it yourself straight
Xfrom the possibilities.
X
XThe format of a password table for a length is:
X
X   # of matches: 43355
X  Primary Guess: PASS1  Checksum good!
X
XThe "Primary Guess" means that all these characters are the best guesses for
Xeach position.  The numbers above them are the number of occurences of each
Xcharacter.  In the above example, there were 4 places that WPCRACK attempted to
Xdetermine the first character of the password.  "P" turned up 4 times.  "A"
Xturned up in the second position 3 times.  The number of times per character
Xvaries because the known bytes are not evenly distributed.
X
XIn this case, the checksum of the primary guess matched, so it shows that.
X
XNow if we have a case where there are multiple character possibilities for
Xone or more positions, you will see something like this:
X
XPassword length of 15 with 20% confidence level:
X
X   # of matches: 111111101100121
X  Primary Guess: FWII/N E'  Y];  (Incomplete!)
X
X   # of matches: 100010000100001
X     Alternates: Z   ^K    ^G    ?
X
XIn this case, there are three characters that we have no clue about (the ones
Xwith zeros above them in the primary guess.  There is another line of counts
Xand characters below it - these are more possibilities.  Here, the first
Xcharacter of the password is equally likely to be a "F" or "Z".  A more useful
Xuseful result might be something like this if you force table mode:
X
X   # of matches: 43353
X  Primary Guess: HELLA  Checksum bad!
X
X   # of matches: 00002
X     Alternates:     O
X
XIn this case, three times the last character turned out to be "A", and twice it
Xwas "O".  "O" was the correct letter, apparently, for then it would spell
X"HELLO".  Table mode lets you see this, although the "no missing characters"
Xdisplay should have picked the "HELLO" correctly as well.  I'm sure there is
Xsome good use for this...  It's too nifty to leave out and makes a good
Xsafety net.
X
X
X
XNotes
X-------------------------------------------------------------------------------
X  The longer the password used, the less chance of determining all of it.
XLuckily, most people don't use passwords greater than 13 characters, and
XCRACKWP should get anything less than that.  And when CRACKWP can't figure it
Xout, you will be shown all of the password that it can figure out and you might
Xbe able to do something with what's shown, just like a crossword puzzle.
X
X  If any of the 22 bytes I assumed are constant change, then things get
Xhairy!  This is why I do all the contortions with voting, table display, etc.
XUnless the change is really drastic, CRACKWP should be able to find it anyway.
X
X  I'd suggest the folks at Word Perfect use a different method in version 6.0!
XFor a product such as this, it's really bad that it's so insecure.  This is
Xgood for those of us who forget our passwords, but bad for someone who thinks
Xtheir data is safe.
X
X  I haven't tried this on any Word Perfect 5.x other than the IBM version.  If
Xyou have it for another computer and are interested in getting a version for
Xit, and you have InterNet access, send me the following:  a UUENCODED (short)
XWord Perfect 5.x file, a UUENCODED version of the file after it has been
Xsaved with a password, and a letter saying hello and the password you used.
XSee my address below.
X
X
X
XReferences
X-------------------------------------------------------------------------------
X[1] "WordPerfect for IBM Personal Computers" WordPerfect Corporation, 1989
X[2] Bennet,J "Analysis of the Encryption Algorith used in the Word Perfect
X    Word Processing Program" 1987, "Cryptologia" Vol XI, No. 4. pp 206-210
X[3] Bergen, H.A. and Caelli, W. J. "File Security in WordPerfect 5.0" 1990,
X    "Cryptologia"
X
X
X==============================================================================
XYou can reach me at my InterNet address of rdippold@qualcomm.com or contact
Xme at one of the fine boards below:
X
XBoard                   Phone                    My Username
X-------------------------------------------------------------
XComputorEdge On-Line    (619) 573-1635           SYSOP .
XFarstar                 (619) 225-1775           Iceman
XMaiden Spectrum         (619) 566-4165           Iceman
XRadio-Active            (619) 268-9625           Iceman
XThe Void                (619) 455-5957           Iceman
X
END_OF_FILE
echo shar: 1 control character may be missing from \"'wpcrack.doc'\"
if test 14549 -ne `wc -c <'wpcrack.doc'`; then
    echo shar: \"'wpcrack.doc'\" unpacked with wrong size!
fi
# end of 'wpcrack.doc'
fi
if test -f 'wpcrack.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'wpcrack.c'\"
else
echo shar: Extracting \"'wpcrack.c'\" \(20292 characters\)
sed "s/^X//" >'wpcrack.c' <<'END_OF_FILE'
X/*---------------------------------------------------------------------
X//  WPCRACK - Copyright (c) 1990, 91 Ron Dippold 10/11/91
X//  Attempt to crack a Word Perfect 5.x file
X//
X//  Distribute this source code _only_ with the unmodified documentation.
X//  Feel free to add your own files if you make improvements.  If you do,
X//  please do the courtesy of mailing me the changes at one of the
X//  places listed in the documentation.  Here's my files:
X//      README.TXT    - Quick info
X//      WPUNCRYP.DOC  - Info about the decryption program
X//      WPUNCRYP.EXE  - MS-DOS executable
X//      WPCRACK.DOC   - Info about the password cracking program
X//      WPCRACK.EXE   - MS-DOS executable
X//----------------------------------------------------------------------
X
X#include <stdio.h>
X
X#define SEEK_SET 0
X
X#define MAX 18
X#define N 5
X
XFILE *f;
X
Xtypedef unsigned char byte;
Xtypedef unsigned int  word;
X
Xbyte buf1[48];
Xbyte *buf;
X
Xword csum1, csum, missing, mi;
X
X// known bytes
Xbyte known[32] = {
X  0xfb, 0xff, 0x05, 0x00, 0x32, 0x00, 0x99, 0x99,
X  0x00, 0x00, 0x99, 0x99, 0x99, 0x00, 0x00, 0x00,
X  0x42, 0x00, 0x00, 0x00, 0x99, 0x00, 0x99, 0x00,
X  0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x99, 0x99 };
X
Xbyte source[MAX][N][MAX];   // source table from known bytes
Xchar pass[MAX][N][MAX];     // possible passwords
Xchar tots[MAX][MAX];        // totals and matches for each password position
Xchar mats[MAX][MAX];
Xint  conf[MAX];             // confidence for each length
X
Xchar chr[N];                // character and count for each char
Xbyte cnt[N];
X
Xchar fmt[N][MAX];         // characters for output
Xbyte cnts[N][MAX];
X
Xint order[MAX];             // order by confidence
Xint thresh=80;              // threshold of confidence
Xint num=0;                  // number of lengths that beat threshold
X
Xbyte pp;
Xchar c;
X
Xbyte idx[MAX];              // current guess
Xchar cur[MAX];
X
Xint i, j, k, l, t, len, tot, match, deep;
Xint do_dec=0, do_table=0;
X
Xvoid docs() {
X  printf( " To attempt to crack a Word Perfect 5.x file, the syntax is:\n\n" );
X  printf( "   WPCRACK (-d) (-t) <filename> ( <threshold> )\n\n" );
X  printf( " <filename> is the Word Perfect file to crack.\n" );
X  printf( " <threshold> is the percentage confidence threshold over which\n" );
X  printf( "    a length is considered to be a possibility.\n" );
X  printf( "    From 0 (all) to 100 (exact match), default 80.\n" );
X  printf( " The -d switch will force WPCRACK to the decimal values of\n");
X  printf( "    each character of an answer as well.  Not pretty.\n");
X  printf( " The -t switch will force WPCRACK to print all answers in table\n");
X  printf( "    form with all possibilities, ignoring checksums.\n");
X  printf( "\n\n To decrypt the file with the password, run WPUNCRYP on it.\n");
X  exit( 1 );
X}
X
Xvoid main( int argc, char **argv )
X{
X  printf( "\nWPCRACK 1.0 - Copyright (c) 1990,91 Ron Dippold\n\n" );
X
X  if( argc < 2 || argc > 5 ) {
X    docs();
X  }
X
X  i=1;
X
X  while( i<argc && argv[i][0] == '-' ) {
X    if( argv[i][1] == 'd' ) {
X      do_dec = 1;
X    } else {
X      if( argv[i][1] == 't' ) {
X        do_table = 1;
X      } else {
X        docs();
X      }
X    }
X    i++;
X  }
X
X  if( i>= argc ) {
X    docs();
X  }
X
X  if( i == argc-2  ) {
X    sscanf( argv[i+1], "%d", &thresh );
X    if( thresh<0 || thresh>100 ) {
X      docs();
X    }
X  }
X
X  if( !( f = fopen( argv[i], "rb" ))) {
X    printf( "  Could not open file %s\n", argv[i] );
X    exit( 2 );
X  }
X
X  fseek( f, 0L, SEEK_SET );
X  if( fread( buf1, 1, 4, f ) != 4) {
X    fclose( f );
X    printf( "  Couldn't read 4 bytes from file %s!\n", argv[i] );
X    exit( 3 );
X  }
X
X  if( buf1[0]!=0xff || buf1[1]!=0x57 || buf1[2]!=0x50 || buf1[3]!=0x43 ) {
X    printf( "%x, %x, %x, %x\n", buf1[0], buf1[1], buf1[2], buf1[3] );
X    fclose( f );
X    printf( "  The identification bytes in this file are not those of a\n" );
X    printf( "  Word Perfect 5.x file.\n" );
X    exit( 4 );
X  }
X
X  if( fread( &buf1[4], 1, 44, f ) != 44 ) {
X    fclose( f );
X    printf( "  Couldn't read the first 48 bytes of the file - garbaged!\n" );
X    exit( 5 );
X  }
X  fclose( f );
X
X  if( buf1[12]==0 && buf1[13]==0 ) {
X    printf( "  The file claims it is not encrypted.\n" );
X    printf( "  We'll continue anyway, but take note!\n\n%c",7 );
X  }
X
X  csum1 = (buf1[12]<<8) + buf1[13];
X
X  buf = &buf1[16];
X
X//  printf( "Building known text table.\n" );
X  for( i=1; i<MAX; i++ ) {
X    for( j=0; j<N; j++ ) {
X      for( k=0; k<i; k++ ) {
X        pp = j*i+k;
X        while ( pp<= 31 ) {
X          if( known[pp] == 0x99 ) {
X            pp+=i; continue;
X          }
X          tot = 0;
X          if( j>0 ) for( l=j-1; l>=0; l-- ) {
X            if( pp == source[i][l][k] ) {
X              tot++;
X            }
X          }
X          if( tot ) {
X            pp+=i;
X          } else {
X            break;
X          }
X        }
X        if ( pp>31 ) pp = 0xFF;
X        source[i][j][k] = pp;
X      }
X    }
X  }
X
X//  printf("Trying password lengths from 1 to %d\n", MAX-1 );
X  for( len=1; len<MAX; len++ ) {
X//printf( "Len %2d:\n", len );
X    for( j=0; j<N; j++ ) {
X      for( k=0; k<len; k++ ) {
X        l = source[len][j][k];
X        if( l != 0xFF && known[l] != 0x99 ) {
X          pp = ( len+l+1 ) ^ known[l] ^ buf[l];
X          if( pp>127 || (pp>='a' && pp<='z')) pp = 0;
X//putchar( pp );
X        } else {
X          pp = 0;
X//putchar( '_');
X        }
X        pass[len][j][k] = pp;
X      }
X//printf( "\n" );
X    }
X
X    // move all good choices to the top
X    for( k=0; k<len; k++ ) {
X      for( j=0; j<N-1; j++ ) {
X        if( pass[len][j][k] == 0 ) {
X          for( l=j; l<N-1; l++ ) {
X            pass[len][l][k] = pass[len][l+1][k];
X          }
X          pass[len][N-1][k] = 0;
X        }
X      }
X    }
X
X    tot=match=0;
X    for( k=0; k<len; k++ ) {
X      tots[len][k] = mats[len][k] = 0;
X      for( j=1; j<N; j++ ) {
X        if( pass[len][j][k] != 0 ) {
X          tots[len][k]+=j;
X          for( i=0; i<j; i++ ) {
X            if( pass[len][j][k] == pass[len][i][k] ) {
X              mats[len][k]++;
X            }
X          }
X        }
X      }
X      tot+=tots[len][k];
X      match+=mats[len][k];
X    }
X
X    if( tot ) {
X      conf[len] = match*100 / tot ;
X    } else {
X      conf[len]=0;
X    }
X  }
X
X//  printf( "Sorting results\n\n" );
X  for( i=0; i<MAX; i++ ) {
X    order[i] = i;
X  }
X
X  // so its a bubble sort.  go to hell.
X  for( i=MAX-1; i>0; i-- ) {
X    for( j=1; j<i; j++ ) {
X      if( conf[order[j]] < conf[order[j+1]] ) {
X        k = order[j]; order[j] = order[j+1]; order[j+1] = k;
X      }
X    }
X  }
X
X  for( num=MAX-1; num>0; num-- ) {
X    if( conf[order[num]]>=thresh ) {
X      break;
X    }
X  }
X
X  printf( "%d length%c meet%c the %d%% initial confidence threshold.\n",
X    num, num==1 ? ' ':'s', num==1 ? 's':' ', thresh );
X
X  if( num == 0 ) {
X    printf( "  Try lowering the threshold.\n");
X    printf( "  Or maybe you need a new version of this to match a Word Perfect change.\n" );
X    exit( 2 );
X  }
X
X  for( l=1; l<=num; l++ ) {
X    len = order[l];
X    printf( "\nPassword length of %d with %d%% confidence level:\n\n",
X      len, conf[len] );
X
X    deep=0;
X    // for each character
X    for( k=0; k<len; k++ ) {
X      for( j=0; j<N; j++ ) {
X        fmt[j][k]=' ';
X        cnts[j][k]=0;
X      }
X
X      // run a character vote
X      for( j=0; j<N; j++ ) {
X        chr[j]=cnt[j]=0;
X      }
X      for( j=0; j<N; j++ ) {
X        if( pass[len][j][k] != 0 ) for(i=0; i<N; i++ ) {
X          if( !chr[i] ) {
X            chr[i] = pass[len][j][k];
X          }
X          if( pass[len][j][k] == chr[i] ) {
X            cnt[i]++;
X            break;
X          }
X        }
X      }
X      for( i=N-1; i>0; i-- ) {      // sort to find most popular
X        for( j=0; j<i; j++ ) {
X          if( cnt[j] < cnt[j+1] ) {
X            t = cnt[j]; cnt[j] = cnt[j+1]; cnt[j+1] = t;
X            t = chr[j]; chr[j] = chr[j+1]; chr[j+1] = t;
X          }
X        }
X      }
X
X      if( cnt[0] == 0 ) {           // no letters
X        fmt[0][k]='_';
X        cnts[0][k]=0;
X      } else {
X        for( j=0; j<N; j++ ) {
X          if( cnt[j] ) {
X            cnts[j][k]=cnt[j];
X            fmt[j][k]=chr[j];
X            if( deep<j) deep = j;
X          }
X        }
X      }
X    }
X
X    // Find any missing letters
X    missing = 0; mi=-1;
X    for( k=0; k<len; k++ ) {
X      if( !cnts[0][k] ) {
X        missing++;
X        mi = k;
X      }
X    }
X
X    if( missing > 1 || do_table ) {
X      if( missing > 1) {
X        printf("  More than one letter is missing - showing all possibilities.\n");
X      } else {
X        printf("  Displaying table form as requested\n");
X      }
X
X      // show in table form
X      for( j=0; j<=deep; j++ ) {
X        printf( "   # of matches: " );
X        for( k=0; k<len; k++ ) {
X          putchar( cnts[j][k] ? cnts[j][k]+'0' : '0' );
X        }
X        if( j==0 ) {
X          printf( "\n  Primary Guess: " );
X        } else {
X          printf( "\n     Alternates: " );
X        }
X        csum = 0;
X        for( k=0; k<len; k++ ) {
X          putchar( cnts[j][k] ? fmt[j][k] : ' ' );
X          if( cnts[j][k] ) {
X            csum = ( (csum >> 1) | ( csum << 15) ) ^ ( fmt[j][k]<<8 );
X          }
X        }
X        if( j==0 ) {
X          if (missing) {
X            printf("  (Incomplete!)");
X          } else {
X            if( csum == csum1 ) {
X              printf("  Checksum good!");
X            } else {
X              printf("  Checksum bad!");
X            }
X          }
X        }
X
X        if( do_dec ) {
X          printf( "\n                 " );
X          for(k=0; k<len; k++) {
X            printf("%d ", cnts[j][k] ? fmt[j][k] : 0);
X          }
X        }
X
X        // if only one missing character, extrapolate
X        if( missing == 1 && j==0 ) {
X          for( i=0; i<128; i++ ) {
X            csum = 0;
X            fmt[0][mi] = i;
X            for( k=0; k<len; k++ ) {
X              csum = ( (csum >> 1) | ( csum << 15) ) ^ ( fmt[0][k]<<8 );
X            }
X            if( csum == csum1 && ( i<'a' || i>='z' )) {
X              printf( "\n   Extrapolated: " );
X              for( k=0; k<len; k++ ) putchar( fmt[j][k] );
X              printf( "   -- Good Checksum");
X              if( do_dec ) {
X                printf( "\n                 " );
X                for(k=0; k<len; k++) printf("%d ", fmt[j][k]);
X              }
X            }
X          }
X        }
X
X        printf( "\n\n" );
X      }
X    } else {
X
X      for( k=0; k<len; k++ ) {
X        idx[k]=0;
X        cur[k]=fmt[0][k];
X      }
X
X      if( missing == 1 ) {
X        cur[mi] = ' ';
X        printf( "  " );
X        for( k=0; k<len; k++ ) putchar( cur[k] );
X        printf( "\n  ");
X        for( k=0; k<len; k++ )
X          putchar( k == mi ? '^' : ' ');
X        printf( "\n The missing character will be extrapolated from the checksum\n\n");
X      } else {
X        printf( " Only possibilities with good checksums will be listed.\n\n");
X      }
X
X      do {
X        if( missing == 0 ) {
X
X          // none missing - generate,  check checksum
X          csum = 0;
X          for( k=0; k<len; k++ ) {
X            csum = ( (csum >> 1) | ( csum << 15) ) ^ ( cur[k]<<8 );
X          }
X          if( csum == csum1 ) {
X            printf( "  " );
X            for( k=0; k<len; k++ ) {
X              putchar( cur[k] );
X            }
X            printf( "   -- Good Checksum\n");
X            if( do_dec ) {
X              printf( "   Dec: " );
X              for( k=0; k<len; k++ ) {
X                printf( "%d ", cur[k] );
X              }
X              printf( "\n");
X            }
X          }
X        } else {
X
X          // checksum missing - generate
X          tot = 0;
X          for( i=0; i<128; i++ ) {
X            csum = 0;
X            cur[mi] = i;
X            for( k=0; k<len; k++ ) {
X              csum = ( (csum >> 1) | ( csum << 15) ) ^ ( cur[k]<<8 );
X            }
X            if( csum == csum1 && ( i<'a' || i>='z' )) {
X              tot++;
X              printf( "  " );
X              for( k=0; k<len; k++ ) putchar( cur[k] );
X              printf( "   -- Good Checksum\n");
X              if( do_dec) {
X                printf("  ");
X                for( k=0; k<len; k++ ) printf("%d ",cur[k]);
X                printf("\n");
X              }
X            }
X          }
X          if( !tot ) {
X            printf("  No valid passwords found\n" );
X          }
X        }
X
X        // next combination
X        k=len-1;
X        while( k>=0 ) {
X          if( cnts[idx[k]+1][k]==0 || cur[k]== N-1 ) {
X            idx[k]=0;
X            k--;
X          } else {
X            idx[k]++;
X            break;
X          }
X        }
X
X      } while( k>=0 );
X    } /* table or checksum */
X}
X}
X
X
X/*----------------------------------------------------------------------
X//  WPUNCRPY - Copyright (c) 1990, 91 Ron Dippold 10/11/91
X//  Decrypt a Word Perfect file once you've got the password
X//
X//  Distribute this source code _only_ with the unmodified documentation.
X//  Feel free to add your own files if you make improvements.  If you do,
X//  please do the courtesy of mailing me the changes at one of the
X//  places listed in the documentation.  Here's my files:
X//      README.TXT    - Quick info
X//      WPUNCRYP.DOC  - Info about the decryption program
X//      WPUNCRYP.EXE  - MS-DOS executable
X//      WPCRACK.DOC   - Info about the password cracking program
X//      WPCRACK.EXE   - MS-DOS executable
X//--------------------------------------------------------------------*/
X
X#include <stdio.h>
X#include <string.h>
X
X#define SEEK_SET 0
X
XFILE           *fin, *fout;
Xchar           *inname, *outname, *pass;
X
Xtypedef unsigned char byte;
Xtypedef unsigned int word;
X
Xword            ver5 = 0;	/* version 4 or 5 */
Xword            override = 0;	/* override checksum */
Xword            pause = 0;	/* pause on screen */
Xword            text = 0;	/* text mode */
Xword            soft = 0;	/* translate CR as a space in text mode */
X
Xlong            tstart;		/* start of text position */
Xlong            pos;		/* position in file */
Xword            lines, chars;	/* lines of text */
X
Xword            plen;		/* password length */
Xword            csum, csum1;	/* checksum */
X
Xword            len;		/* bytes to read */
Xword            idx;		/* index into buffer */
X
Xbyte            buf[4096], c;
X
Xword            pidx;		/* index down password */
Xbyte            xmask;		/* XOR mask */
X
Xint             i, j, k;
X
Xvoid 
Xdocs()
X{
X	printf(" To uncrypt a Word Perfect 4.x or 5.x file, the syntax is:\n\n");
X	printf("    WPUNCRYP (-o) (-p) (-b) w|t \"password\" <input file> (<output file>) \n\n");
X	printf("  Use the 'w' command to decrypt <input file> into <output file>\n");
X	printf("    in Word Perfect format.  This option is meaningless if <output\n");
X	printf("    file> is not given.\n");
X	printf("  Use the 't' command to decrypt the text portion of <input file>.\n");
X	printf("    This will not remove any embedded formatting codes, you can do\n");
X	printf("    that yourself!\n");
X	printf(" If <output file> is not given, the decrypted text will be shown to\n");
X	printf("    the screen.\n");
X	printf(" The optional -o switch will force the decrpytion even if the checksum\n");
X	printf("    of the password doesn't match the checksum of the file.\n");
X	printf(" The optional -p switch will make the make WPUNCRYP attempt to pause\n");
X	printf("    after every screen of output to the screen.\n");
X	printf(" In text output mode, both hard and soft line breaks are output as a\n");
X	printf("    CR/LF.  The optional -b will translate the soft break as a space.\n");
X	exit(1);
X}
X
Xvoid 
Xmain(int argc, char **argv)
X{
X	printf("\nWPUNCRYP 1.0 - Copyright (c) 1990,91 Ron Dippold\n\n");
X
X	if (argc < 4 || argc > 7) {
X		docs();
X	}
X	i = 1;
X	override = pause = text = soft = 0;
X
X	while (i < argc && argv[i][0] == '-') {
X		switch (argv[i][1]) {
X		case 'o':
X		case 'O':
X			override = 1;
X			break;
X		case 'p':
X		case 'P':
X			pause = 1;
X			break;
X		case 'b':
X		case 'B':
X			soft = 1;
X			break;
X		default:
X			docs();
X		}
X		i++;
X	}
X
X	if (i >= argc - 2) {
X		docs();
X	}
X	if (argv[i][1] != '\0')
X		docs();
X	if (argv[i][0] == 't') {
X		text = 1;
X	} else {
X		if (argv[i][0] == 'w') {
X			text = 0;
X		} else {
X			docs();
X		}
X	}
X	i++;
X
X	pass = argv[i++];
X	plen = strlen(pass);
X
X	inname = argv[i++];
X
X	if (i < argc) {
X		outname = argv[i];
X		pause = 0;
X	} else {
X		outname = NULL;
X		text = 1;
X	}
X
X	if (i > argc) {
X		docs();
X	}
X	if (!text) {
X		soft = 0;
X	}
X	if (!(fin = fopen(inname, "rb"))) {
X		printf("  Could not open file %s\n", inname);
X		exit(2);
X	}
X	fseek(fin, 0L, SEEK_SET);
X	if (fread(buf, 1, 4, fin) != 4) {
X		fclose(fin);
X		printf("  Couldn't read 4 bytes from file %s!\n", inname);
X		exit(3);
X	}
X	if (buf[0] == 0xff && buf[1] == 0x57 && buf[2] == 0x50 && buf[3] == 0x43) {
X		printf("  This is a Word Perfect 5.x file.\n");
X		ver5 = 1;
X	} else {
X		if (buf[0] == 0xfe && buf[1] == 0xff && buf[2] == 0x61 && buf[3] == 0x61) {
X			printf(" This is a Word Perfect 4.1 encrypted file.\n");
X		} else {
X			fclose(fin);
X			printf("  The identification bytes in this file are not those of a\n");
X			printf("  Word Perfect 5.x or 4.x encrpyted file.\n");
X			exit(4);
X		}
X	}
X
X	if (ver5) {
X		if (fread(&buf[4], 1, 12, fin) != 12) {
X			fclose(fin);
X			printf("  Couldn't read the first 16 bytes of the file - garbaged!\n");
X			exit(5);
X		}
X		if (buf[12] == 0 && buf[13] == 0) {
X			printf("  The file claims it is not encrypted.\n");
X			fclose(fin);
X			exit(6);
X		}
X	}
X	/* Now read the checksum */
X	if (ver5) {
X		csum1 = (buf[12] << 8) + buf[13];
X	} else {
X		if (fread(&buf, 1, 2, fin) != 2) {
X			printf("  Couldn't read checksum - short file!\n");
X			fclose(fin);
X			exit(7);
X		}
X		csum1 = (buf[4] << 8) + buf[5];
X	}
X
X	csum = 0;
X	for (i = 0; i < plen; i++) {
X		if (pass[i] >= 'a' && pass[i] <= 'z') {	/* convert to upper case */
X			pass[i] -= 'a' - 'A';
X		}
X		csum = ((csum >> 1) | (csum << 15)) ^ (pass[i] << 8);
X	}
X
X	if (csum != csum1) {
X		if (override) {
X			printf("  Warning!  Password checksum does not match file checksum!\n");
X			printf("  Any output is suspect.\n");
X		} else {
X			printf("  The password checksum does not match the checksum in the file.\n");
X			printf("  If you are convinced it is right, check the docs for the -o option.\n");
X			fclose(fin);
X			exit(8);
X		}
X	}
X	/* get start of text */
X	if (ver5) {
X		tstart = *((long *) (&buf[4]));
X		pos = 16;
X	} else {
X		tstart = pos = 6;
X	}
X
X	if (outname) {
X		if (!(fout = fopen(outname, text ? "wt" : "wb"))) {
X			printf("  Could not open file %s\n", outname);
X			exit(2);
X		}
X		fseek(fout, 0L, SEEK_SET);
X	} else {
X		fout = NULL;
X	}
X
X	if (ver5 && fout && !text) {
X		buf[12] = buf[13] = 0;	/* write out first part of WP5.1 file */
X		fwrite(&buf[0], 1, 16, fout);
X	}
X	pidx = 0;		/* start of password */
X	xmask = plen + 1;	/* start at password length+1 */
X
X	if (ver5) {		/* get rid of printer info, etc. */
X		while (pos < tstart) {
X			if (tstart - pos > 4096) {
X				len = 4096;
X			} else {
X				len = tstart - pos;
X			}
X			if (fread(buf, 1, len, fin) != len) {
X				printf("\n  Unexpected end of file for %s.\n", inname);
X				fclose(fin);
X				fclose(fout);
X				exit(20);
X			}
X			for (idx = 0; idx < len; idx++) {	/* now decrypt */
X				buf[idx] ^= pass[pidx++] ^ xmask++;
X				if (pidx == plen)
X					pidx = 0;
X			}
X			if (fout && !text) {
X				if (fwrite(buf, 1, len, fout) != len) {
X					printf("\n  Couldn't write %d bytes to %s.\n", len, outname);
X					fclose(fin);
X					fclose(fout);
X					exit(20);
X				}
X			}
X			pos += len;
X		}		/* while */
X	}			/* ver5 */
X	idx = len = 0;		/* no data yet */
X	lines = chars = 0;	/* no lines shown */
X
X	do {
X
X		/* read from file as necessary */
X		if (idx >= len) {
X			if (len > 0 && fout) {
X				if (fwrite(buf, 1, len, fout) != len) {
X					printf("\n  Couldn't write %d bytes to %s.\n", len, outname);
X					fclose(fin);
X					fclose(fout);
X					exit(20);
X				}
X			}
X			len = fread(buf, 1, 4096, fin);
X			pos += len;
X			if (len == 0) {
X				break;	/* all done with file */
X			}
X			idx = 0;
X		}
X		c = buf[idx] ^= pass[pidx++] ^ xmask++;
X		if (pidx == plen)
X			pidx = 0;
X
X		if (text) {
X			if (c == 0xA9)
X				buf[idx] = '-';	/* translate annoying dashes */
X			if (c == 0x0D) {	/* handle soft break */
X				if (soft) {
X					buf[idx] = ' ';
X				} else {
X					buf[idx] = '\n';
X				}
X			}
X		}
X		if (!fout) {
X			printf("%c", c = buf[idx]);
X			if (c == '\n' || ++chars == 80) {
X				lines++;
X				chars = 0;
X				if (lines == 24 && pause) {
X					getch();
X					lines = 0;
X				}
X			}
X		}
X		idx++;
X
X	} while (1);
X
X	fclose(fin);
X	if (fout)
X		fclose(fout);
X}
END_OF_FILE
if test 20292 -ne `wc -c <'wpcrack.c'`; then
    echo shar: \"'wpcrack.c'\" unpacked with wrong size!
fi
# end of 'wpcrack.c'
fi
echo shar: End of shell archive.
exit 0
-- 
Pete Shipley:
email: shipley@berkeley.edu             Flames:  cimarron@postgres.berkeley.edu
Spelling corections: /dev/null          Quote: "Anger is an energy"


-- 
********************************************************** 
* J.C.A. Wevers                *   The only nature of    * 
* johan@blade.stack.urc.tue.nl *   reality is physics.   * 
**********************************************************