by
CASIMIR
Extract of Savard Software propaganda : "Using Crypt-o-Text, you can "scramble" your message so that it is unreadable... at least until it is "unscrambled" by the person receiving your message. Additionally, you can apply password-protection so that the scrambled message can only be unscrambled by someone who knows the password you used to scramble it."
Start Winice from DOS prompt.
Launch COT, enter a message and encrypt it with, let's say: "CASIMIR" :-)
Usually, to find out what a prg is doing with my input (fake pwd, fake registration number...whatever you want), i set a breakpoint (BPX) on functions such as: GetDlgItemText, GetDlgItemInt,... and i land smack into the routine busy checking my input.
Try it: you'll discover IT DOESN'T WORK in this case. I still haven't figured
out exactly how COT gets the input. Nevermind, since prg stupidly beeps when
input is wrong, we perform following actions:
CODE:OFFSET 0137:00434E22 JZ 00434E55 // 0 => good guy 434E24 PUSH 10 // bad guy, let's beep him 434E26 CALL USER32!MessageBeep // code that triggered Winice 434E2B * PUSH 00
Having a look up in code (smaller offset values) and setting some BPX we find interesting stuff:
0137:00434CAA * CMP EAX,[EBP-20] // EAX=bogus pwd lenght, [EBP-20]=good pwd 434CAD JNE 434E24 lenght (7)We now know good_pwd_lenght (if you don't believe me, try with bogus pwd of various lenght)! We'll find out later in which location it is "hidden" in crypted file.
Wouldn't it be nice if COT would perform the following actions:
We can assume that comparaison takes place between CS:434CAD and CS:434E22.
Wandering for a while, the CMP sequence shows up in all her Glory :
(enter "1212121" as a bogus pwd)
0137:00434E17 MOV EAX,[EBP-34] -> 31.32.31.32.31.32.31 ("1212121") 434E1A MOV EDX,[EBP-38] -> D2.54.D1.A5.C2.7A.26 434E1D CALL 00403700 // trace it 434E22 JZ 00434E55 // 0 => good guy 0137:00403700 PUSH BPX . . 403729 MOV ECX,[ESI] -> 31.32.31.32.31.32.31 ("1212121") 40372B MOV EBX,[EDI] -> D2.54.D1.A5.C2.7A.26 // funny string 40372D CMP ECX,EBXOh my God! There is NO echo of good pwd in memory. Instead of "CASIMIR", we get that funny string: D2.54.D1.A5.C2.7A.26. But if we enter good pwd, we obtain 43.41.53.49.4D.49.52 ("CASIMIR") instead of meaningless funny_string. It seems to me that author of COT didn't want good pwd to be uncrypted every time a bogus pwd is entered, it would have been a huge security'breach (and i would not been writting this file at 3AM).
That is THE question... Setting dozens of BPRs, i finally find out that funny_string pops up "by parts" on memory location DS:[68FB41 68FB42]. It seems to be written down here by the following code sequence:
0137:00434D78 MOV EAX,100 CALL 00402984 XOR EDX,EDX // clean EDX MOV DL,[EBP-3F] XOR EAX,EDX // EAX=6E EDX=BC MOV [EBP-3F],AL // AL=D2 : 1° byte of funny_string MOV EAX,100 CALL 00402984 XOR EDX,EDX // clean EDX MOV DL,[EBP-3E] XOR EAX,EDX // EAX=4A EDX=1E MOV [EBP-3E],AL // AL=54 : 2° byte of funny_string MOV EAX,100 CALL 00402984 MOV EBX,EAX XOR EAX,EAX // clean EAX MOV AL,[EBP-3D] XOR EBX,EAX // EBX=8A EAX=5B 0137:00434DB3 MOV [EBP-3D],BL // BL=D1 : 3° byte of funny_stringThis routine is called several times, until funny_string is constitued. We get some extra bytes if pwd lenght is not a multiple of 3 (e.g: if pwd lenght=7, we obtain 7 valid bytes + 2 useless bytes). Now let's try with correct pwd ("CASIMIR") :
LEFT RIGHT CHECK VECTOR VECTOR VECTOR EAX=FF xor EDX=BC => AL=43 ("C") EAX=5F xor EDX=1E => AL=41 ("A") EBX=08 xor EAX=5B => BL=53 ("S") . . . . . . . . ....and so on. Only LEFT-hand vector (our funny_string) changes, RIGHT-hand vector remains the same. Situation sounds pretty obvious to me : left-hand vector is calculated as a function of input, whereas right-hand vector is calculated as a function of good pwd (stored in crypted file, where else???). Then check_vector is built by "xorization" of left and right vectors, and compared against input.
=> GOOD input : input = check_vector => BAD input : input != check_vector5. The Making of left vector
Don't be so impatient and stop complaining! We are nearly done, COT is already, humm, 25% dead...
OK, what is the duty of input in making of left vector? Let's investigate the mysterious calls to 00402984. Here is what we get :1° call 0137:00402984 IMUL EDX,[0043902C],08088405 // [0043902C]=00000857 INC EDX // EDX=FF0505B3 MOV [0043902C],EDX // EDX=FF0505B4 MUL EDX // EDX=100*EDX MOV EAX,EDX // EDX=FF (higher-weight byte) 0137:00402999 RET 2° call 0137:00402984 IMUL EDX,[0043902C],08088405 // [0043902C]=FF0505B4 INC EDX // EDX=5FA9EC84 MOV [0043902C],EDX // EDX=5FA9EC85 MUL EDX // EDX=100*EDX MOV EAX,EDX // EDX=5F 0137:00402999 RETWe trace a third call too, just to have fun (and make sure there are no tricks):3° call 0137:00402984 IMUL EDX,[0043902C],08088405 // [0043902C]=5FA9EC85 INC EDX // EDX=086E3299 MOV [0043902C],EDX // EDX=086E329A MUL EDX // EDX=100*EDX MOV EAX,EDX // EDX=08 0137:00402999 RETPseudo-code:
for(i=0;i<pwd_lenght;i++) { Seed = (08088405*Seed + 1); left_vector[i] = 100*Seed; }Conclusion : left_vector depends entirely on FIRST value -the "Seed"- present at location [0043902C] (in our case : 0x857).Subsidiary question : WHO built the Seed ?
6. The Seed
Pay attention to memory location DS:0043902C and so on. Is this address already referenced in code? Yes Sir! Just after pwd lenght check [part 2 of this file], we have:
0137:00434CAA CMP EAX,[EBP-20] JNE 434E24 MOV EAX,[EBP-34] -> "CASIMIR" CALL 00433978 0137:00434CBB MOV [0043902C],EAX // EAX=857Seed seems to come from call to 433978. As usual, we take our machine-gun and go for it:0137:00433978 PUSH EBP . . XOR EBX,EBX // EBX . . 4339A2 MOV EDX,EAX // pwd_lenght (7) TEST EDX,EDX JLE 004339CC MOV EAX,00000001 *****> 4339AD MOV ECX,[EBP-04] * MOVZX ECX,BYTE PTR[ECX+EAX-01] // ECX=43,41,53,49,4D,49,52 * IMUL ECX,EAX ("CASIMIR") * JNO 004339BF * CALL 00402B10 * 4339BF ADD EBX,ECX // EBX=1*43+2*41+3*53+4*49+5*4D+6*49+7*52=857 * JNO 004339C8 * CALL 00402B10 * 4339C8 INC EAX * DEC EDX **************< JNZ 004339AD // check if pwd'end reachedLoop is executed pwd_lenght times, summing each pwd'ascii value weighted by its position in pwd :
OK, everybody here? Did someone get lost in the code? Now we stop tearing off COT'guts (just for a while), and we lay down, a good cup of Tea at the hand (don't fall asleep, the nice part of the story is coming up!!!).
Here are actions performed by COT:
Seed_MIN = (1+2+...+pwd_lenght)*code_MIN = 0.5*pwd_lenght*(pwd_lenght+1)*code_MIN Seed_MAX = 0.5*pwd_lenght*(pwd_lenght+1)*code_MAXCOT v1.24 accepts up to 45 characters as input, so we obtain:
Seed_MIN=0.5*45*46*32 Seed_MAX=0.5*45*46*247 =33120 =255645So, with pwd_lenght=45 (worst situation from our cracker point-of-view), we ONLY have (Seed_MAX-Seed-MIN)= 222525 first Seeds possible!!! What a SMALL number!!! Weakness of COT comes -mostly- from this ridiculous 222525...
Let's quickly write an ugly C prg performing the following steps:
Well, we can now find out what pwd was used to encrypt file, but for that purpose we must first find out pwd_lenght and right_vector using Winice... Quite boring and time-costing, so let's finish the work.
Finding pwd_lenght doesn't require any reversing, tracing, or stack-fishing... Go into COT and encrypt various files using different pwd_lenght. WATCH IT!!! And if you can't see it LOOK CLOSER!!! YES, those guys just wrote it down, between 3° and 4° slash, without any encryption of any kind. Never seen such self-confidence...For instance :
pwd_lenght=10 : CoT/0121/01/10/7 pwd_lenght=45 : CoT/0124/03/45/3We just open crypted file and read it.
Finding of right_vector is -a little- more complicated. First of all, go into COT, and encrypt the message "LONG LIFE TO CASIMIR" using "CASIMIR" as pwd. Here is what we get:
********************** START Crypt-o-Text ********************** CoT/0121/01/7/20 CLy68KoYUEBAFiqBzkfCTGjGiH-+k-LHaQnpio+Z1 CoT/5225 ********************** END Crypt-o-Text **********************Do you remember portion of code studied in Part 4 (i'm sure you do)? It was something like this:
0137:00434D78 MOV EAX,100 ... MOV DL,[EBP-3F]->SS:0068FB41 ... MOV DL,[EBP-3E]->SS:0068FB42 ... MOV AL,[EBP-3D]->SS:0068FB43 ... 0137:00434DB3 MOV [EBP-3D],BLHumm...It's certainly worth spying those memory locations. Doing so, we discover that something is going on in following code sequence:
0137:00433D71 MOV DL,[EBP-04] -> 4C ("L") CALL 0040358C MOV EAX,[EBP-14] -> 4C ("L") MOV EDX,[004388F8] -> AaBbCc... CALL 0040387C MOV EBX,EAX // EAX=17 SUB EBX,1 // EBX=16 ... 00433D97 IMUL EBX,[004388DC] // EBX=40000*16=580000 ... 00433DA4 MOV DL,[EBP-03] -> 79 ("y") CALL 0040358C MOV EAX,[EBP-14] -> 79 ("y") MOV EDX,[004388F8] -> AaBbCc... CALL 0040387C SUB EAX,1 // EAX=31 ... 00433DC4 IMUL DWORD PTR [004388E4] // EAX=1000*31=31000 ... 00433DD1 ADD EBX,EAX // EBX=EBX+EAX=580000+31000=5B1000 ... 00433DDD MOV DL,[EBP-02] -> 36 ("6") CALL 0040358C MOV EAX,[EBP-14] -> 36 ("6") MOV EDX,[004388F8] -> AaBbCc... CALL 0040387C SUB EAX,1 // EAX=3A ... 00433DFD IMUL DWORD PTR [004388EC] // EAX=40*3A=E80 ... 00433E0A ADD EBX,EAX // EBX=EBX+EAX=5B1000+E80=5B1E80 ... 00433E16 MOV DL,[EBP-01] -> 38 ("8") CALL 0040358C MOV EAX,[EBP-14] -> 38 ("8") MOV EDX,[004388F8] -> AaBbCc... CALL 0040387C SUB EAX,1 // EAX=3C ... 00433E36 IMUL DWORD PTR [004388F0] // EAX=1*3C=3C ... 0137:00433E43 ADD EBX,EAX // EBX=EBX+EAX=5B1E80+3C=5B1EBC5B1EBC??? BC,1E,5B!!! Guess who's popping up? Right_vector himself! It is being extracted from crypted file, starting on second line just after "C": Ly68...
Ascii values from crypted file are read 4 by 4, until right_vector is built.
But a question remains: how do we know that, for instance, an "8" (38) read in crypted file will produce an "<" (3C)? To learn more about it, we investigate suspicious calls to 0040387C. Before tracing call, notice that EDX points on the following DATA structure:
DS : 00432E3C Aa Bb Cc Dd Ee Ff Gg Hh DS : 00432E4C Ii Jj Kk Ll Mm Nn Oo Pp DS : 00432E5C Qq Rr Ss Tt Uu Vv Ww Xx DS : 00432E6C Yy Zz 01 23 45 67 89 +-OK, now we can follow call:
0137:0040387C TEST EAX,EAX ... 0040388B MOV ECX,[EDI-04] // ECX=40 (size of look-up table shown above) PUSH EDI MOV EDX,[ESI-04] // EDX=1 (we are looking for 1 character) DEC EDX JS 004038B0 MOV AL,[ESI] ->38 ("8"): we are looking for "8" in table INC ESI SUB ECX,EDX JLE 004038B0 REPNZ SCASB ->search 38 JNZ 004038B0 ->NOT FOUND (file corrupted?) ... 004038B8 MOV EAX,EDI // EDI=432E79=our actual position in table ("9") SUB EAX,EDX // EDX=432E3C=first position in table ("A")So : EAX=3D (offset of "8" + 1). After CALL 0040387C, we perform a SUB EAX,1 to obtain correct offset.
Final formula is therefore:
right_vector=40000*offset("L")+1000*offset("y")+40*offset("6")+1*offset("8")This system offers COT the possibility of transforming whatever right_vector into an ASCII string that any word-processor can handle...
Now we add those improvement to our ugly prg and we obtain nice CRACKCOT.EXE that will run provided ONLY with the name of the encrypted file!!! (lazy guys will appreciate).
Compiled with Borland C++ v5.0 and running on a 150MHz Pentium, pwd
are found in a matter of seconds by CRACKCOT.EXE.
For a given pwd_lenght, the WORST pwd you can think of is the one with
the HIGHEST first Seed value, because it will be tested at the very end
of iteration. Corresponding ASCII character to code_MAX is : "÷" (ALT 246)
PWD PROCESSING TIME 1 ÷ <1s 10 ÷÷÷÷÷÷÷÷÷÷ 3s 20 ÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷ 10s 30 ÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷ 21s 40 ÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷ 36s 45 ÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷ 46s input : name of crypted file output : password and equivalent ALT sequenceBONUS : a valid Registration Number for COT v1.21, found during my "training period"
!!!!!!!!!!!!!!!!!!!!!! CRACKCOT.CPP !!!!!!!!!!!!!!!!!!!!! #include <stdio.h> #include <io.h> #include <fcntl.h> #include <errno.h> #include <dir.h> #include <string.h> #include <dos.h> #include <malloc.h> #include <stdlib.h> #include <time.h> #include <conio.h> const int car_nb=136; // number of characters in car set // car[0->135] set of characters used static int car[car_nb]={0x20,0x21,0x22,0x23, 0x24,0x25,0x26,0x27, 0x28,0x29,0x2A,0x2B, 0x2C,0x2D,0x2E,0x2F, 0x30,0x31,0x32,0x33, 0x34,0x35,0x36,0x37, 0x38,0x39,0x3A,0x3B, 0x3C,0x3D,0x3E,0x3F, 0x40,0x41,0x42,0x43, 0x44,0x45,0x46,0x47, 0x48,0x49,0x4A,0x4B, 0x4C,0x4D,0x4E,0x4F, 0x50,0x51,0x52,0x53, 0x54,0x55,0x56,0x57, 0x58,0x59,0x5A,0x5B, 0x5C,0x5D,0x5E,0x5F, 0x60, 0x7B, 0x7C,0x7D,0x7E,0x7F, 0x83, 0x9F, 0xA1,0xA2,0xA3, 0xA4,0xA5,0xA6,0xA7, 0xA8,0xA9,0xAA,0xAB, 0xAC,0xAD,0xAE,0xAF, 0xB0,0xB1,0xB2,0xB3, 0xB4,0xB5,0xB6,0xB7, 0xB8,0xB9,0xBA,0xBB, 0xBC,0xBD,0xBE,0xBF, 0xC0,0xC1,0xC2,0xC3, 0xC4,0xC5,0xC6,0xC7, 0xC8,0xC9,0xCA,0xCB, 0xCC,0xCD,0xCE,0xCF, 0xD0,0xD1,0xD2,0xD3, 0xD4,0xD5,0xD6,0xD7, 0xD8,0xD9,0xDA,0xDB, 0xDC,0xDD,0xDE,0xDF, 0xF7}; // alt[0->135] look-up table for ASCII code static int alt[car_nb]={0x20,0x21,0x22,0x23, 0x24,0x25,0x26,0x27, 0x28,0x29,0x2A,0x2B, 0x2C,0x2D,0x2E,0x2F, 0x30,0x31,0x32,0x33, 0x34,0x35,0x36,0x37, 0x38,0x39,0x3A,0x3B, 0x3C,0x3D,0x3E,0x3F, 0x40,0x41,0x42,0x43, 0x44,0x45,0x46,0x47, 0x48,0x49,0x4A,0x4B, 0x4C,0x4D,0x4E,0x4F, 0x50,0x51,0x52,0x53, 0x54,0x55,0x56,0x57, 0x58,0x59,0x5A,0x5B, 0x5C,0x5D,0x5E,0x5F, 0x60, 0x7B, 0x7C,0x7D,0x7E,0x7F, 0x9F, 0x98, 0xAD,0xBD,0x9C, 0x0F,0xBE,0xB3,0x15, 0xF9,0xB8,0xA6,0xAE, 0xAA,0xF0,0xA9,0xEE, 0xF8,0xF1,0xFD,0xFC, 0xEF,0xE6,0x14,0xFA, 0xF7,0xFB,0xA7,0xAF, 0xAC,0xAB,0xF3,0xA8, 0x85,0xA0,0x83,0xC6, 0x84,0x86,0x91,0x80, 0x8A,0x82,0x88,0x89, 0x8D,0xA1,0x8C,0x8B, 0xD0,0xA4,0x95,0xA2, 0x93,0xE4,0x94,0x9E, 0x9B,0x97,0xA3,0x96, 0x81,0xEC,0xE7,0xE1, 0xF6}; const int xor_nb=0x40; // number of elements in xor_table // xor_table[0->3F] alphabeta table COT uses when creating right_vec[] from // crypted text static int xor_table[xor_nb]={0x41,0x61,0x42,0x62, 0x43,0x63,0x44,0x64, 0x45,0x65,0x46,0x66, 0x47,0x67,0x48,0x68, 0x49,0x69,0x4A,0x6A, 0x4B,0x6B,0x4C,0x6C, 0x4D,0x6D,0x4E,0x6E, 0x4F,0x6F,0x50,0x70, 0x51,0x71,0x52,0x72, 0x53,0x73,0x54,0x74, 0x55,0x75,0x56,0x76, 0x57,0x77,0x58,0x78, 0x59,0x79,0x5A,0x7A, 0x30,0x31,0x32,0x33, 0x34,0x35,0x36,0x37, 0x38,0x39,0x2B,0x2D}; // PROTOTYPES void Hello(void); int Get_target(void); int Read_pwd_lenght(int,int,char **,int *); void Calc_right_vec(int,char *,int,int,int **); int Position(char); void Read_xor_ascii(int,char *,int,int,char **); void Calc_min_max(int,int *,int *); void Calc_left_vec(unsigned int *,unsigned int *); int In_list(int,int *); void Print_pwd(int,int *,time_t); void Chrono(void); void Wait_key(); main() { int fn; // crypted file'handle int pwd_lenght; // password lenght (1 to 45 characters) const int buf_size=1000; // buffer size char *buf; // buffer to store first 1000 characters of file int pos_buf; // current position in buffer int code_min; // smaller code possible int code_max; // larger code possible int code; // code_min <= code <= code_max int code_chk; // code check, let us know if we are on the good way unsigned int seed; // number used to crypt character (first seed = code) int i; // General Purpose variable time_t time_start; // chrono starting unsigned int *left_vec; // left_vector built using seed int *right_vec; // values used by COT to uncrypt pwd'characters int *pwd; // password uncrypted int *car_pos; // character'position in set Hello(); fn=Get_target(); // init buf buf=(char *)malloc(sizeof(char)*buf_size); pwd_lenght=Read_pwd_lenght(fn,buf_size,&buf,&pos_buf); if(pwd_lenght==0) { printf("\nNO PWD required to uncrypt file...\n"); Wait_key(); exit(0); } right_vec=(int *)malloc(sizeof(int)*pwd_lenght); Calc_right_vec(pwd_lenght,buf,buf_size,pos_buf,&right_vec); // Init_pwd pwd=(int *)malloc(sizeof(int)*pwd_lenght); car_pos=(int *)malloc(sizeof(int)*pwd_lenght); left_vec=(int *)malloc(sizeof(int)*pwd_lenght); Calc_min_max(pwd_lenght,&code_min,&code_max); time_start=time(NULL); printf("\nProcessing"); // main loop for(code=code_min;code<=code_max;code++) { // ini //printf("\ncode : %x",code); Chrono(); seed=code; code_chk=0x00; for(i=0x00;i<pwd_lenght;i++) { Calc_left_vec(&seed,&left_vec[i]); pwd[i]=(left_vec[i]^right_vec[i]); // XORization //printf("\npwd : %x",pwd[i]); if(In_list(pwd[i],&car_pos[i])!=1) // is it a member of car set ? { break; // not a valide character, let's try next code } else // OK, character valide { code_chk+=((i+1)*pwd[i]); if(code_chk>code) { break; // characters too big, let's try next code } else { if(i==(pwd_lenght-1)) { // OK, pwd lenght reached, so let's check // if the pwd built is the good one if(code_chk!=code) { break; // no good, let's try next code } else { // PASSWORD FOUND !!! //printf("found"); Print_pwd(pwd_lenght,car_pos,time_start); exit(0); } } } } } } } /*************************************************************/ /********************** FUNCTIONS ****************/ /*************************************************************/ void Hello(void) { printf("\n Cracker for CRYPT-o-TEXT v1.21 & v1.24\n"); } /**********************************************************************/ /* try to open crypted file (must be in the SAME directory) */ /* - success : return file'handle */ /* - fail : exit prg */ int Get_target(void) { unsigned char buf[100]; int fn; printf("\nFile to uncrypt [e.g: SECRETXT.COT]? "); gets(buf); // try to open file fn=open(buf,O_BINARY|O_RDONLY); switch(fn) { case -1:printf("\nFILE NOT FOUND! (file to crack MUST be in SAME"); printf("\ndirectory as CRACKCOT.EXE; file'name CAN NOT exceed"); printf("\n8 characters; DO NOT USE accentuated characters;"); printf("\nand last but not least: DO NOT forget file'extension!)\n"); Wait_key(); exit(0); default: /*printf("\nOK, FILE FOUND")*/; return(fn); } } /**********************************************************************/ /* password'lenght is written WITHOUT ANY ENCRYPTION (!!!) in file, */ /* always at same position, i.e between 3° and 4° slash */ /* */ /* ********************** START Crypt-o-Text ********************* */ /* CoT/0121/01/4/14 */ /* CHiWyEBAtOSy0ROktxyEUGAuffQJb */ /* CoT/2683 */ /* ********************** END Crypt-o-Text ********************* */ /* */ /* here, for instance, pwd lenght = 4 ( DECIMAL notation) */ int Read_pwd_lenght(int fn,int buf_size,char **buf,int *pos_buf) { int i,j; int pwd_lenght=0; int slash_nb=0; // count "/" int digit_nb=0; // pwd'lenght digit number int base=1; // read first 1000 characters of file read(fn,*buf,buf_size); close(fn); // find 3° "/" for(i=0;i<buf_size;i++) { if((*buf)[i]==0x2F) // "/" ? { slash_nb++; if(slash_nb==3){break; /* OK, ready to read pwd lenght */} } } if(slash_nb<3){printf("\nSORRY, NOT A VALID FILE\n");Wait_key();exit(0);} // let's count nb of digit(s) betweeen 3° and 4° "/" i++; while((*buf)[i+digit_nb]!=0x2F) {digit_nb++;} for(j=1;j<digit_nb;j++) {base*=10;} // get pwd lenght for(j=0;j<digit_nb;j++) { pwd_lenght+=(((*buf)[i+j]-0x30)*base); // conversion hex->dec base=(base/10); } // save our position in buffer *pos_buf=(i+digit_nb); //printf("\nPWD LENGHT : %d\n",pwd_lenght); return(pwd_lenght); } /**********************************************************************/ /* right_vec[pwd_lenght] is built using ascii values stored in */ /* crypted file. */ /* those values are read by blocks of 4, and a block of 4 */ /* produces only 3 values to fill right_vec. */ /* ex: if we have, let's say, pwd lenght = 4, we'll need 4 values */ /* in right_vec (2 groups of 3, values 5° and 6° are not used), */ /* so we'll have to read: 2*4=8 values in file. */ void Calc_right_vec(int pwd_lenght,char *buf,int buf_size,int pos_buf, int **right_vec) { int ascii_nb; // nb of values to read from file char *xor_ascii; // values taken from file int xor_nb; // number of right_vec values built (xor_nb>=pwd_lenght) int *xor_temp; // keep ALL right_vec values built int block_nb; // number of blocks required to build right_vec[] int block; // current block int *xor_pos; // ascii'values positions in xor_table int i; int code; block_nb=(pwd_lenght/3)+1; ascii_nb=4*block_nb; xor_ascii=(char *)malloc(sizeof(char)*ascii_nb); Read_xor_ascii(ascii_nb,buf,buf_size,pos_buf,&xor_ascii); xor_nb=3*block_nb; xor_temp=(int *)malloc(sizeof(int)*xor_nb); xor_pos=(int *)malloc(sizeof(int)*4); // 4 values in block for(block=0;block<block_nb;block++) { for(i=0;i<4;i++) {xor_pos[i]=Position(xor_ascii[4*block+i]);} code=(0x40000*xor_pos[0]+0x1000*xor_pos[1]+0x40*xor_pos[2]+xor_pos[3]); // now we extract 3 right_vec values from code xor_temp[3*block+2]=(code/0x10000); code=(code-0x10000*xor_temp[3*block+2]); xor_temp[3*block+1]=(code/0x100); code=(code-0x100*xor_temp[3*block+1]); xor_temp[3*block]=code; } // fill right_vec (pwd_lenght values to enter) for(i=0;i<pwd_lenght;i++) { (*right_vec)[i]=xor_temp[i]; } } /**********************************************************************/ /* return position of "value" in xor_table */ int Position(char value) { int i; for(i=0;i<xor_nb;i++) { if(xor_table[i]==value) {return(i);} } } /**********************************************************************/ /* ascii values are located after the second "C", just under "CoT" */ /* using precedent example, we'll read: HiWy EBAt */ void Read_xor_ascii(int ascii_nb,char *buf,int buf_size,int pos_buf, char **xor_ascii) { int i,j,k; int found=0; // we go on reading file, just after pwd_lenght, looking for "C" for(i=pos_buf;i<buf_size;i++) { if(buf[i]==0x43) // "C" ? { found=1; break; /* OK, ready to read ascii_nb values */ } } if(!found) {printf("\nSORRY, NOT A VALID FILE\n"); Wait_key(); exit(0);} // fill xor_ascii[] i++; k=0; for(j=i;j<(i+ascii_nb);j++) { (*xor_ascii)[k]=buf[j]; k++; } } /**********************************************************************/ /* return code min and max as a function of pwd_lenght and set of */ /* characters used */ void Calc_min_max(int pwd_lenght,int *code_min,int *code_max) { // code_min=1*MIN(car)+2*MIN(car)+...+pwd_lenght*MIN(car) // code_min=1*MAX(car)+2*MAX(car)+...+pwd_lenght*MAX(car) // 1+2+..+n=0.5*n*(n+1) *code_min=0.5*(pwd_lenght)*(pwd_lenght+1)*car[0]; *code_max=0.5*(pwd_lenght)*(pwd_lenght+1)*car[car_nb-1]; } /**********************************************************************/ /* build left_vec[i] */ /* also generate the new seed that will be used to build */ /* left_vec[i+1] */ void Calc_left_vec(unsigned int *seed,unsigned int *left_vec) { *seed=((*seed)*0x08088405+1); *left_vec=(*seed)/0x1000000; } /**********************************************************************/ /* check if uncrypted character belongs to character set */ /* - yes => return(1) and character'position in set */ /* - no => return(0) */ int In_list(int pwd_car,int *pos) { int i; for(i=0;i<car_nb;i++) { if(pwd_car==car[i]){*pos=i; return(1);} /* character found */ } return(0); // character not found } /**********************************************************************/ /* Print password decrypted, along with equivalent "ALT" sequence */ void Print_pwd(int pwd_lenght,int *pos,time_t time_start) { int i; time_t t; t=time(NULL); printf("[%3.0lfs]",difftime(t,time_start)); printf("\n\n ALT sequence: "); for(i=0;i<pwd_lenght;i++) { printf("[%d]",alt[pos[i]]); if((i+1)%10==0) {printf("\n ");} } printf("\n\n PWD: >>>"); for(i=0;i<pwd_lenght;i++) { printf("%c",alt[pos[i]]); } printf("<<<\n\n"); printf("\n\nbrought to You by CASIMIR!\n\n"); printf("BONUS!!! to register CoT v1.21 enter : 111292*859100"); Wait_key(); } /**********************************************************************/ /* chronometer */ void Chrono(void) { static time_t t1; time_t t2; t2=time(NULL); if(difftime(t2,t1)>1){printf(".");t1=time(NULL);} // ¤ } /***********************************************************************/ /* wait for key pressed */ void Wait_key() { printf("\n\n"); printf(" XXXXXXXXXXXXXXXXXXXXXXX\n"); printf(" X hit any key to eXit X\n"); printf(" XXXXXXXXXXXXXXXXXXXXXXX\n"); while(!kbhit()) {/* wait until key pressed */} } !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Here is the executable.
If you have any questions, suggestions, metaphysical interrogations, feel free to contact Casimir
Of course, nothing would have been possible without our beloved Winice, nice and powerful debugger for WIN 95 by Nu-Mega. (ICE95.ZIP [739787]). If you experiment some display problems when switching from WIN 95 to Winice, try to set WIN 95 video mode to 16 colors.