CRACKING OF ENCRYPT-IT FOR WINDOWS

by

CASIMIR

Part A

Other Essays by Casimir
  • Cracking of Crypt-o-Text v1.21 & v1.24
  • Correspondence From Casimir On Reversing Turbo Encrypto
  • Cracking of WinXFiles
  • The Cracking of File Locker
  • The Cracking of Keeper
  • The Cracking of Gregory Braun's Crypto v3.5
  • The Cracking of SecurityPlus!
  • The Cracking of MasterKey v1.02/1.05
  • Software by MaeDae Enterprises
    (You might need to use FTP Search to find the software.)

    This file presents a cracking procedure for both proprietary algorithms found in this software:

    Source code (C) for a cracker is also provided.


    Map of the essay:

    Introduction

    Why a crack? First of all, for the challenge. This software is well written, with useful and uncommon features (such as statistical analysis). Those guys at MaeDae Enterprises didn't steal their $59. So they deserved a good crack!

    Second reason: there are so many "proprietary" algos hidden out there that i believe some "natural selection" is needed (a cryptographic algorithm is called proprietary if its internal working, its code, is kept secret). People using proprietary algos in their commercial software usually claim that the secret wrapping their cipher is just one more layer of protection for the user... WRONG! No algorithm can resist reversing and disassembling very long, the secret comedy is just a way to delay breaking time (and to keep guys like me busy late at night).

    ********Keeping an algo secret protects the seller, not the user***********

    In my opinion, only "fair" algos, i.e. whose internal working is perfectly known because it has been published by the author (such as BlowFish or DES) but can not be broken should be used in ciphers.

    WARNING 1

    This file DOES NOT give a crack for DES, DES+CBC, or BlowFish (sorry, too hard for me {:-( ). One day, maybe...

    WARNING 2

    FOR EDUCATIONAL PURPOSES ONLY !

    WARNING 3

    This crack is TOUGH, and cost me a hell of work (even if it's always great fun to break a cipher!). So if you don't want your brain to be damaged forever, you should:

    No knowledge in cryptography science required!

    Strategy

    We WON'T analyze encrypted text files with statistical techniques (how many times a character appears, etc...) to find out its contents. This would be Cryptanalysis, the "classical" way to break ciphers, by the way... Instead, we will focus exclusively on the PASSWORD (pwd). Our Holy Quest will be to recover the correct pwd for encrypted text file. Of course, then you can have EIW itself decrypting file for you {;-).

    PART A - Reversing of EIW

    We will reverse Encrypt-It International release v3.30 for Win95 (EIW.EXE : 699392 bytes). International and U.S./Canadian releases (v3.23, v3.30, v4.40 and probably others) work basically the same, so crack will work on both.

    1. Warm up

    Launch Winice, we will spy our target (EIW.EXE) "live". (You can disassemble prg if you want, but i didn't need to do so.) Create a text file (file.txt) in Notepad, and write inside: ABCDEF (6 characters, nothing else).

    Encrypt file.txt using 3 Way Proprietary method. Enter CASIMIR as a password. Watch out! Pwd is case-sensitive. EIW will create encrypted file: file.~00. Now follow the decryption process: select file, enter twice a bogus pwd: 11111 , but don't press OK yet.

    2. What's going on with my input?

    Before, we must figure out which Windows function will collect our input (11111). So we set BreakPoints on common Win32 functions that usually deal with inputs:

    Now you can press OK, and watch which function you caught. In this case, it's the famous GetWindowTextA that shows up:
    CS:43E76D CALL GetWindowTextA

    Just before call, EIW places on the stack 3 arguments (PUSH x, PUSH y, PUSH z). The 2° arg is the memory location where input will be stored. I get:
    CS:43E766 PUSH 6EBE88

    So if you dump data at DS:6EBE88 AFTER calling function, you will see your input.

    This function is called a 2° time, so we have a 2° copy of input at: DS:6EC088

    The function is called a 3° time, but nothing pops up. OK, now we want to know what the input is used for by EIW, so we must survey EACH location! This can be done by setting Break Points on memory read/write:

    Winice pops up at:
    CS:BFF7117E REPNZ SCASB //computes string'length at mem location DS:6EBE88
    CS:BFF7117E REPNZ SCASB //computes string'length at mem location DS:6EC088
    CS:BFF786A9 CMP [ECX],AL //checks if strings at DS:6EBE88 and DS:6EC088 are the same
    and so on...

    Then something more interesting shows up:
    CS:BFF711CF REPNZ MOVSD //copy input from DS:6EBE88 (ESI) to DS:4724FC (EDI)

    We have to survey the new input'location:

    (Hint: you can disable BPRs on old locations, nothing more will show up there...)

    Winice will pop up several times before we reach the useful portion of code. I don't know exactly what input is used for while we make for interesting location. But i wrote down what i found so you don't get lost {;-) :
    *CS:4039.. ////???
    *CS:403A.. //compute sum of input'characters
    *CS:40E9.. //???

    Now WAKE UP for a while, critical section ahead! Winice pops up at location CS:402FA9, at the beginning of a routine (see below) which performs various tasks:

    2.1 Task 1: Computation of Block_size

    00402F90 push ebx // routine_1 begin
    *
    *
    *
    :00402F9E mov esi,[esp+20] // points to input stored in DS:4724FC
    :00402FA2 mov ebx,eax
    :00402FA4 add esp,00000004
    :00402FA7 xor edx,edx // edx=0
    :00402FA9 mov al,[esi] // al=first char of input (00000031)
    :00402FAB xor ecx,ecx // ecx=0
    :00402FAD test al,al // al==0 => end_of_input reached
    :00402FAF je 00402FC1
    :00402FB1 and eax,000000FF // mask => eax=000000??
    :00402FB6 add edx,eax // compute sum of input'chars
    :00402FB8 mov al,[ecx+esi+01] // read next char
    :00402FBC inc ecx // stores number of input'chars read
    :00402FBD test al,al // al==0 => end_of_input reached
    :00402FBF jne 00402FB1
    :00402FC1 lea eax,[edx+ecx] // eax=chars'sum + chars'nb (F5+5=FA)
    :00402FC4 cdq // edx=0
    :00402FC5 xor eax,edx
    :00402FC7 sub eax,edx
    :00402FC9 and eax,0000000F // mask => eax=0000000? (0000000A)
    :00402FCC xor eax,edx
    :00402FCE sub eax,edx
    :00402FD0 and eax,0000000F
    :00402FD3 add eax,00000002 // eax+=2 (0000000C)

    We'll see just below that eax acts as a "Block_size" value during iterations:

    With input=11111, we obtain: So we "only" have 16 possibilities for Block_size value (hex notation):

    2.2 Task 2: Permutations in encrypted file

    EIW modifies original encrypted file before using it. As modifications depend on Block_size value, which itself depends on input, it's a good way to deter casual crackers {;-).

    :00402FD6 xor ebp,ebp // ebp=0
    :00402FD8 lea ecx,[eax+ebp] // ecx=eax=Block_size
    :00402FDB cmp ecx,edi // edi=0x78 (always)
    :00402FDD jle 00402FE3
    :00402FDF mov eax,edi
    :00402FE1 sub eax,ebp
    :00402FE3 xor ecx,ecx // ecx=0
    :00402FE5 test eax,eax
    :00402FE7 jle 00403004
    :00402FE9 mov edx,[esp+14] // points to DS:6EF61C

    Dump data at DS:6EF61C, you'll see:
    A6 CF 1A 5B C6 7D 30 E7 94 27 68 CA 5B 4D B3 E5 2D DE 95 48 FF AC 6B 28
    Block 1
    Block 2
    0x0C characters
    0x0C characters

    You know file.~00 by heart, don't you? Well, this IS the header of file.~00 (actually, file'listing starts at DS:6EF614). Now, EIW will modify it by blocks of characters (1 block="Block_size" characters).
    :00402FED mov esi,eax // esi=eax=Block_size
    :00402FEF lea edi,[ebp+edx] // points to current file'block
    :00402FF3 mov dl,[ecx+edi] // reads 1 char
    :00402FF6 inc ecx // counts chars read from block
    :00402FF7 mov [ebx+esi-01],dl // copy 1 char to buffer
    :00402FFB dec esi // esi DECREASES, so string is copied BACK to FRONT
    :00402FFC cmp ecx,eax // ecx==Step => stop
    :00402FFE jl 00402FF3
    :00403000 mov edi,[esp+18] // edi=0x78 (always)
    :00403004 xor ecx,ecx // ecx=0
    :00403006 test eax,eax
    :00403008 jle 0040301D
    :0040300A mov edx,[esp+14] // points to DS:6EF61C
    :0040300E lea esi,[ebp+edx] // points to block to substitute for buffer
    :00403012 mov dl,[ecx+ebx] // read 1 char from buffer
    :00403015 mov [ecx+esi],dl // write 1 char to block
    :00403018 inc ecx // counts chars read from block
    :00403019 cmp ecx,eax // ecx==Block_size => stop
    :0040301B jl 00403012
    :0040301D add ebp,eax //ebp points to next block
    :0040301F cmp ebp,edi // still block(s) to read?
    *
    *
    *
    :00403030 ret // routine_1 end

    If you dump DS:6EF61C just after leaving routine, you get:
    CA 68 27 94 E7 30 7D C6 5B 1A CF A6 28 6B AC FF 48 95 DE 2D E5 B3 4D 5B
    1 kcolb
    2 kcolb
    0x0C characters
    0x0C characters

    Task 3: Building of Sum_vector

    We go on using "BPR DS:4724FC DS:472500 RW"... We find out that input plays again a first-rate role in the following routine:
    :00402EF0 push ebx // routine_2 begin
    :00402EF1 push ebp
    :00402EF2 mov ebp,[esp+14] // ebp points to DS:4724FC
    :00402EF6 push esi
    :00402EF7 xor ebx,ebx // ebx=0
    :00402EF9 xor esi,esi // esi=0
    :00402EFB mov al,[ebp+00] // al=first char of input (00000031)
    :00402EFE push edi
    :00402EFF test al,al // al==0 => end_of_input reached
    :00402F01 je 00402F13
    :00402F03 and eax,000000FF // mask => eax=000000??
    :00402F08 add ebx,eax // compute sum of input'chars
    :00402F0A mov al,[esi+ebp+01] // read next char
    :00402F0E inc esi // stores number of input'chars read
    :00402F0F test al,al // al==0 => end_of_input reached
    :00402F11 jne 00402F03

    This part probably reminds you of Block_size'computation part, huumm? It's the same stuff: input'sum (i.e. sum of input'characters) is stored in ebx, and input'length (i.e. number of characters) is in esi. Let's see what's next...
    :00402F13 mov eax,ebx
    :00402F15 mov ecx,00000032
    :00402F1A cdq
    :00402F1B idiv ecx
    :00402F1D mov edi,[4*edx+0046B548]
    :00402F24 push edi
    :00402F25 call 00427150
    :00402F2A add esp,00000004
    :00402F2D xor ecx,ecx // ecx=Vector_sum'length=0
    :00402F2F test edi,edi
    :00402F31 mov [esp+1C],eax
    :00402F35 jle 00402F57
    :00402F37 mov eax,ecx // eax=0
    :00402F39 cdq // edx=0
    :00402F3A idiv esi // edx=(ecx)mod(input'length)
    :00402F3C mov al,[edx+ebp] //al=input'char n (n=0,1,2,3,4,0,1,2,...)
    :00402F3F add ebx,eax // ebx+=char n
    :00402F41 and ebx,000000FF // mask => ebx=000000??
    :00402F47 mov dl,bl
    :00402F49 xor dl,al // Sum_vect[ecx]=(dl)xor(al)
    :00402F4B mov eax,[esp+1C] // eax points to DS:700078
    :00402F4F mov [eax+ecx],dl // stores value
    :00402F52 inc ecx // Sum_vector'length++
    :00402F53 cmp ecx,edi // edi=71F (always)
    :00402F55 jl 00402F37 // if vector not long enough: go on

    We obtain: (input=11111)

    Note: // "^" = "XOR" ; "&" = "AND" (C syntax)

    Dump DS:700078 after passing CS:402F55, you get:

    This vector is used in the next part of the routine.

    Task 4: Making of Xor_vector

    Xor_vector is built using Sum_vector and permuted text. Hold on, we're nearly done with the reversing of that crap {;-)
    :00402F57 mov ebx,[esp+18] // ebx=78
    :00402F5B xor ecx,ecx // ecx=0
    :00402F5D test ebx,ebx
    :00402F5F jle 00402F7E
    :00402F61 mov esi,[esp+14] // esi points to permuted txt (DS:6EF61C)
    :00402F65 mov eax,ecx
    :00402F67 cdq
    :00402F68 idiv edi
    :00402F6A mov eax,[esp+1C] // eax points to Sum_vector (DS:700078)
    :00402F6E mov dl,[edx+eax] // dl=Sum_vector[ecx]
    :00402F71 mov al,[ecx+esi] // al=Permuted_txt[ecx]
    :00402F74 xor al,dl //Xor_vect[ecx]=al=Sum_vect[ecx]^Permuted_txt[ecx]
    :00402F76 mov [ecx+esi],al // al replaces permuted_txt[ecx]
    :00402F79 inc ecx // ecx++
    :00402F7A cmp ecx, ebx // end?
    :00402F7C jl 00402F65
    *
    *
    *
    :00402F8F ret // routine_2 end
    So here we have:

    Dump DS:6EF61C after executing CS:402F7C, you'll see:

    OK, now we can leave this portion of code, which has nothing more to tell us. Follow return at CS:402F8F to leave current routine and discover *Hey, YOU!!! If you're still there, please use the word "dromedary" in your reply {;-) (just trying to find out if some human-being can survive this essay...)* what's next.

    Task 5: Making of Cmp_vector

    Cmp_vector is made of Xor_vector and input. Cmp_vector will be checked in the finale compare sequence.

    :00403712 call 00402EF0 //that's the routine_2 we just return from
    :00403717 mov al,[ebx] //al=first character of input
    :00403719 add esp,0000000C
    :0040371C test al,al
    :0040371E mov esi, 00000001
    :00403723 je 0040372D >//if al==0: finished
    :00403725 mov al,[esi+ebx] //al=Input[i]
    :00403728 inc esi //i++
    :00403729 test al,al //if al==0: finished
    :0040372B jne 00403725
    :0040372D dec esi //esi=input'length

    :0040372E xor ecx,ecx //clean ecx
    :00403730 test ebp,ebp
    :00403732 jle 00403995
    :00403738 mov eax,ecx
    :0040373A cdq
    :0040373B idiv esi
    :0040373D mov al,[ecx+edi] //al=Xor_vector[ecx]
    :00403740 mov dl,[edx+ebx] //dl=Input[i] (i=0,1,2,3,4,0,1,2...)
    :00403743 xor al,dl //Cmp_vector[ecx]=al=Xor_vector[ecx]^input[i]
    :00403745 mov [ecx+edi],al //store Cmp_vector at DS:6EF61C
    :00403748 inc ecx
    :00403749 cmp ecx,ebp //done?
    :0040374B jl 00403738
    :0040374D pop edi
    :0040374E pop esi
    :0040374F pop ebp
    :00403750 pop ebx
    :00403751 ret

    So here we have: (input=11111)

    Once again, dump DS:6EF61C when leaving routine (CS:403751):

    OK, now exit Winice (EIW laughs at you 'cause input is wrong, but soon it will pay for its arrogance.). Enter correct pwd ("CASIMIR") and come back to CS:403751. Look at DS:6EF61C:

    Cmp_vector:
    66 69 6C 65 2E 74 78 74 00 00 00 00 00 43 41 53 49 4D 49 52 00
    f      i     l     e   .    t    x    t C    A  S   I   M   I   R

    Cmp_vector'structure is ALWAYS the SAME (when input is correct, of course):
    FILENAME.TXT
    0
    PASSWORD00000000000000000000000000000000
    0
    12 bytes
    1 byte
    40 bytes (123 with U.S. version)
    1 byte
    0x00
    password storage
    0x00

    We'll see later that weakness of EIW results from the use of this "static" data structure {:-)

    Task 6:The Ultimate CMP

    This part of code is the one that lets you in if you provided the good pwd, or throws you out otherwise.
    :0040EB93 add esp,00000010
    :0040EB96 lea esi,[ebp+FFFFFE19] //esi points to byte 14 of Cmp_vector
    :0040EB9C mov eax,004724FC //DS:4724FC=Input
    :0040EBA1 mov dl,[eax] //dl=Input[0],Input[2],...,Input[2*i],...
    :0040EBA3 mov bl,[esi] //bl=Cmp_vect[14],Cmp_vect[14+2],...,[14+2*i],..
    :0040EBA5 mov cl,dl
    :0040EBA7 cmp dl,bl //if Input[2*i]!=Cmp_vect[14+2*i]: GET OUT!!!
    :0040EBA9 jne 0040EBC9
    :0040EBAB test cl,cl //if cl==0: end of Input reached: stop comparison
    :0040EBAD je 0040EBC5
    :0040EBAF mov dl,[eax+01] //dl=Input[1],Input[3],...,Input[2*i+1],...
    :0040EBB2 mov bl,[esi+01] //bl=Cmp_vect[15],Cmp_vect[15+2],.,[15+2*i],.
    :0040EBB5 mov cl,dl
    :0040EBB7 cmp dl,bl //if Input[2*i+1]!=Cmp_vect[15+2*i]: GET OUT!!!
    :0040EBB9 jne 0040EBC9
    :0040EBBB add eax,00000002
    :0040EBBE add esi,00000002
    :0040EBC1 test cl,cl //if cl==0: end of Input reached: stop comparison
    :0040EBC3 jne 0040EBA1
    :0040EBC5 xor eax,eax

    So the final CMP takes place at locations CS:40EBA7 and CS:40EBB7.

    We're done with reversing, now comes the funny part: the breaking!

    Click for PART B - Analysis and Breaking of EIWfor part B

    Copyright June 1998 by Casimir.

    Mail Casimir

    Converted to hypertext by Joe Peschel June 21, 1998.