|
CuteFTP KeyFile Protection
|
Advanced reversing
|
30 October 1998
|
by
The+Q
|
|
|
Courtesy of Fravia's page of
reverse engineering
|
slightly edited
by fravia+ |
fra_00xx 98xxxx handle 1100 NA PC
|
This is a very important essay. A very fine protection is here analyzed and explained by
an outstanding +cracker. It's a reading that all protectors and all crackers should
head and
follow until they understand it. Since CuteFtp is a very good and widespread program,
the keygenerators for this target have already been
released by various scene and warez crackers months ago, so I don't believe we will damage Alex Kunadze publishing
this. Actually the contrary should be true, at least I hope: reading how a good cracker
understands a good protection, you can try to eliminate the 'weak' points of that
same protection...
provided you'r a good programmer, with enough clues. I may add, as last observation, that
CuteFtp is a really outstanding program. I'm using it personally on my windoze machines
(actually I'm using its older version 2.0, because I don't
like to change version every three minutes), and, believe me,
I choose very carefully my programs. Therefore I
do recommend
to all my readers to get this target, crack it black and blue, following this
extremely interesting essay by The+Q and learning a lot in the process, and then
pay for it.
A worthy program and a worthy protection, they all
deserve it.
Alex should head the concluding words by The+Q,
though:As always, I searched for other parts in the target to study, and i found
that cuteftp encryptes the password for the ftps u put on the list. After a simple
look with HexEditor on TREE.DAT u see the password is encypted by a simple XOR.
Very disapointing after having seen the though protection for the key-file.
(Oh well , it's no wonder the programmer doesn't give much when it comes
to protect YOUR files..)
|
|
|
There is a crack, a crack in everything
That's how the light gets in
| |
Rating
|
( )Beginner ( )Intermediate (X)Advanced ( )Expert
|
|
CuteFTP has a good Key-File protection.
Here's we'll study it, and I'll show you a few techniques for reversing algorithms.
CuteFTP KeyFile Protection
Written by
The+Q
About half a year ago I tried to crack cuteftp key-file protection.. unsuccessfully.
I only got a cracking hang-over the following day (a cracking hang-over: when u wake up,
the first and only thing u see is the protection executed FAST and repeatedly. It's like
when ur stuck in a chess game , and u start to see ALL possible solutions at-once..
What can i say .. It's a cool feeling =)
Anyway , I've decided to give it another shoot (Never Say Never!;).
This time with different techniques to solve it - both mathematical and somewhat poetic.
I hope u'll enjoy and learn from this. :)
SoftIce
W32Dasm
Hex-Editor (HIEW is my fav.)
And some backround music (Beethoven, Brahms, R.E.M .. =)
http://www.cuteftp.com
(Any v2.x ; Newest is v2.6 , and I'll refere to it)
CuteFTP has been using a Key-File protection since the beginning (as far as i
know).However the protection scheme has changed in ver2.x . We'll Study this
protection .. and crack it ;)
Btw, have u noticed that all communication software targets have a good protection?
(Terminate, CuteFTP, BulletProof =)
OK , let's start.
Once CuteFTP is running, fire up SoftIce and breakpoint on the CreateFileA Api,
which is used to open files:Bpx CreateFileA do "d *(ss:esp+4)".
Help\About in CuteFTP menu, and SI pops. The data window holds the filename
parameter of the API. Our keyfile name is CuteFtp.Key . After a few P-RETs (F12)
we reach the read file procedure :
:00411DB3 push 100 ; 100h Bytes to read
:00411DB8 lea ecx, dword ptr [esp+34]
:00411DBC push 01
:00411DBE push ecx ; Pointer to Buffer that gets the bytes
:00411DBF call 00439BD0 ; Read Key-File
:00411DC4 add esp, 10
:00411DC7 mov edi, eax ; Eax and edi with the acctualy bytes read
:00411DC9 push esi
:00411DCA call 00439B20 ; Not importent
:00411DCF add esp, 04
:00411DD2 cmp edi, 100
:00411DD8 jge 00411DE6 ; If length of file is 100h bytes (or more) then
; continue with reg check
Ok,quickly make a file named CuteFtp.Key, and fill it with 00-FFh (100h bytes in
total) (Don't do it manualy , code a little prog to do it..:)
Now we show this 'reporter clerence' to the program, so we can goto CS:00411DE6..
:00411DE6 push 19F ; Seed value
:00411DEB call 00439AE0 ; Set seed value
:00411DF0 add esp, 04
:00411DF3 xor esi, esi ; Count=0
:00411DF5 lea edi, dword ptr [esp+0C] ; edi points to the table
:00411DF9 call 00439AF0 ; Play with seed , and produre a 16bit number
:00411DFE movsx edx, ax ; 16bit to 32bit value
:00411E01 xor edx, esi
:00411E03 inc esi ; Inc Count
:00411E04 mov dword ptr [edi], edx ; Update table with number produced in CALL above
:00411E06 add edi, 04 ; Inc table index
:00411E09 cmp esi, 08
:00411E0C jl 00411DF9 ; Produce 8 numbers (32bit each one)
This routine produces a set of constant values . This Set is not linked to our file,
meaning everytime you run the protection scheme it will give out the same set of values
, regurdless of the data in the file.It's simple to test: Set a breakpoint on the file
buffer, and see that this routine doesn't break on it. (Procedures deal with the
parameters given to it, so if the pointer to our file buffer is not input, it's
most likely this procedure won't touch it..).
Notice that we don't care HOW these values are driven.All we care is that it's constant.
It's a 'black box' that gives out the same numbers , if the input (seed) is the same.
Here is this table , which will be refered as Const_Set:
:DD (esp+0C) L 20
0167:0061F2F8 00000571 00002E55 00007277 00006A6B q...U...wr..kj..
0167:0061F308 0000737F 00000BAB 00005646 000074BD s......FV...t..
( ConstSet:Array[1..8] of longint = $571,$2E55,$7277,$6A6B,$737F,$0BAB,$5646,$74BD; )
We continue with the following:
:00411E0E lea eax, dword ptr [esp+0C] ; Points to the Const_Set
:00411E12 lea ecx, dword ptr [esp+22C] ; Points to Table_Head
:00411E19 push eax
:00411E1A call 00429660 ; Copy Const_Set (20h bytes) to [ECX] (-Head of table)
; (The decryption uses this Table_Head and the Table
; that follows)
; [ESP+2C] points to begining of our file buffer. It's maked as File[0]
; As u can guess, [ESP+4C] points to the 21h-st (4C-2C=20h) byte in the file - File[20]
:00411E1F lea ecx, dword ptr [esp+2C] ; File[0]
:00411E23 push 04 ; Decrypt 04*08=20h bytes
:00411E25 push ecx
:00411E26 lea ecx, dword ptr [esp+234] ; Points to Table_Head
:00411E2D call 00429630 ; ** Decryption Procedure
:00411E32 lea edx, dword ptr [esp+2C] ; File[0] after decryption
:00411E36 lea ecx, dword ptr [esp+22C] ; Points to Table_Head
:00411E3D push edx
:00411E3E call 00429660 ; Update Table-head with 20h bytes from decrypted File[0]
:00411E43 lea eax, dword ptr [esp+4C] ; File[20]
:00411E47 push 01 ; Decrypt 01*08=08h bytes
:00411E49 push eax
:00411E4A lea ecx, dword ptr [esp+234] ; Points to Table_Head
:00411E51 call 00429630 ; ** Decryption Procedure
:00411E56 mov ebp, dword ptr [esp+4C] ; File[20] after decryption
:00411E5A lea ecx, dword ptr [esp+54] ; File[28]
:00411E5E push 1B ; Decrypt 1B*08=D8h bytes
:00411E60 push ecx
:00411E61 lea ecx, dword ptr [esp+234] ; Points to Table_Head
:00411E68 call 00429630 ; ** Decryption Procedure
:00411E6D lea edi, dword ptr [esp+54] ; File[28] after decryption - UserName
:00411E71 or ecx, FFFFFFFF
:00411E74 xor eax, eax
:00411E76 lea edx, dword ptr [esp+12C]
:00411E7D repnz ; Search NULL (indicates end of NAME string)
:00411E7E scasb
:00411E7F not ecx
:00411E81 sub edi, ecx
:00411E83 mov eax, ecx
:00411E85 mov esi, edi
:00411E87 mov edi, edx
:00411E89 shr ecx, 02
:00411E8C repz ; Move 'clean' Name to another location
:00411E8D movsd
:00411E8E mov ecx, eax
:00411E90 mov eax, 28
:00411E95 and ecx, 03
:00411E98 repz ; Still moving ..
:00411E99 movsb
:00411E9A xor ecx, ecx
:00411E9C movsx edx, byte ptr [esp+eax+2C] ; [ESP+28+2C] = [ESP+54] => File[28]
:00411EA1 add ecx, edx
:00411EA3 inc eax
:00411EA4 cmp eax, 78
:00411EA7 jl 00411E9C ; Do CheckSum on 50h (78-28) bytes from File[28]
:00411EA9 cmp ecx, ebp
:00411EAB jne 00411EFA ; If ChecSum of Name (File[28]) is not equal
; to value in ebp (File[20]), then NOT REGISTERED
---------------------------------------------------
** Decryption Procedure :
:00429630 push ebx
:00429631 push edi
:00429632 mov edi, dword ptr [esp+10] ; EDI with Number of 8_byte_blocks to decrypt
:00429636 mov ebx, ecx
:00429638 test edi, edi
:0042963A jle 00429650
:0042963C push esi
:0042963D mov esi, dword ptr [esp+10] ; ESI points to block of encrypted bytes
:00429641 push esi
:00429642 mov ecx, ebx
:00429644 call 00429400 ; *** Main decryption routine
:00429649 add esi, 08 ; Next 8_byte_block to decrypt
:0042964C dec edi
:0042964D jne 00429641
:0042964F pop esi
:00429650 pop edi
:00429651 pop ebx
:00429652 ret 0008
Now that you've read it , go and read it again! Try to see the logic behind it,
even before analyzing the decryption routine.(Some comments are added after analyzing,
so u might not understand them , but u can understand the logic in this code).
Before we summerize , lets do some simple checks to figure out general properties
of our decryption routine.It's adviced to do so before a deep analyze on the routine
(which can lead to a cracking hang-over ;).. So analyze in layers.. like in graphics :)
As u have propably figured out, the OUT buffer - where the decrypted bytes are
written- is the IN buffer. Decrypted bytes are written over the input-encrypted
bytes.
Break-point with SI at location **CS:00429642 , and look at the file-buffer
:D DS:ESI
U'll see our data at File[0]: 00 01 02 03 04 05 06 07-08 09 0A 0B 0C 0D 0E 0F
New let it decrypt - trace over with SI (F10) until CS:00429649 .
Look at the data window : 04 55 04 05 00 11 9E 00-08 09 0A 0B 0C 0D 0E 0F
The main decryption routine (***CS:00429400) decrypts 8-bytes (64bit) each time.
The second parameter to the **Decryption procedure is the number of 8_byte_blocks to
decrypt , so when '1B' was pushed (CS:00411E5E), it ment decrypt D8 (1B*08) bytes.
Next let's check if the decryption is also used for encryption .It's very rare, but
it does exist (Y=X xor 3B -> X=Y xor 3B ..).
Breakpoint as before , but now the input will be : 04 55 04 05 00 11 9E 00
Let it decrypt .. and look at the Data window , it's NOT 00 01 02 .. 07 , so
it's not going to be so easy ... GOOD! ;)
Next check is to see if it's inferior to order of execution .
Input : 00 01 02 03 04 05 06 07-00 01 02 03 04 05 06 07
After decryption (twice) : 04 55 04 05 00 11 9E 00-04 55 04 05 00 11 9E 00
Ok , it doen't matter when you decrypt it.
Summerize
1. Decryption procedure properties:
-The main decryption routine decryptes 8_bytes each time.
-Out buffer = In buffer , meaning Decrypted bytes are written over the
input-encrypted bytes.
-Decryption routine is NOT the encryption routine.
-Inferior to order of execution.
2. Keyfile properties:
-Name: CuteFtp.Key
-Length: 100h bytes.
-Format: ------------------------- - - -
New_Table_Head 20h
------------------------- - -
UserName CheckSum 08
------------------------- - - 100h
UserName (Ends with null)
D8h
------------------------- - - -
3. General work of the protection:
-Create a Constant Set of values - 8 32bit (=20h bytes) numbers.
-Copy Constant_Set to Table_Head.
-Table_Head = Decrypted 20h bytes from File[0].
-Correct CheckSum value = Decrypted 8h bytes from File[20] .(Now the decryption
uses the updated Table_Head for decryption)
-UserName = Decrypted D8h bytes from File[28]. Null indicates end of Name string.
-Do CheckSum on UserName.
-If ChecSum of Name (File[28]) is not equal to value in ebp (Correct
CheckSum-File[20]) then NOT REGISTERED.
Ok , so far so good. If u missed anything until here , i suggest u d/l the prog. and
play with it your-self .
Now comes the most importent part : Analyzing the decryption and reversing it.
*** Main decryption routine :
:00429400 push ebx
:00429401 push ebp
:00429402 mov ebp, dword ptr [esp+0C]
:00429406 push esi
:00429407 mov esi, ecx
:00429409 push edi
; EBP points to 8_byte buffer , with the encrypted bytes.
; ESI points to Table_Head
:0042940A mov edi, dword ptr [ebp+00] ; \
:0042940D mov ebx, dword ptr [ebp+04] ; Read the 8 encrypted bytes
; /
:00429410 mov eax, dword ptr [esi+00]
:00429412 add eax, edi
:00429414 push eax
:00429415 call 004293A0 ; ****Decrypt_Value_Function
:0042941A mov edx, dword ptr [esi+04]
:0042941D xor ebx, eax
:0042941F mov ecx, ebx
:00429421 add ecx, edx
:00429423 push ecx
:00429424 mov ecx, esi
:00429426 call 004293A0 ; ****Decrypt_Value_Function
:0042942B mov ecx, dword ptr [esi+08]
:0042942E xor edi, eax
.. Pattern repeats ..
..........
:00429601 mov ecx, dword ptr [esi+04]
:00429606 mov eax, edi
:00429608 add eax, ecx
:0042960A mov ecx, esi
:0042960C push eax
:0042960D call 004293A0 ; ***Decrypt_Value_Function
:00429612 mov ecx, dword ptr [esi+00]
:00429614 xor ebx, eax
:00429616 add ecx, ebx
:00429618 push ecx
:00429619 mov ecx, esi
:0042961B call 004293A0 ; ***Decrypt_Value_Function (This Func is Called 32 times)
:00429620 xor edi, eax
; \
:00429622 mov dword ptr [ebp+00], ebx ; Write the 8 decrypted bytes
:00429625 mov dword ptr [ebp+04], edi ; /
---------------------------------------------------
****Decrypt_Value_Function :
:004293A0 mov eax, dword ptr [esp+04] ; The value pushed to the function
:004293A4 push esi
:004293A5 mov esi, ecx
; ESI points to Table_Head.
; ESI+20 -> Decryption_Table#1 ; ESI+120 -> Decryption_Tabel#2 ..
:004293A7 mov ecx, eax
:004293A9 shr ecx, 18
:004293AC movsx edx, byte ptr [ecx+esi+20]
:004293B1 xor ecx, ecx
:004293B3 mov cl, byte ptr [esp+0A]
:004293B7 shl edx, 08
:004293BA movsx ecx, byte ptr [ecx+esi+120]
:004293C2 or edx, ecx
:004293C4 xor ecx, ecx
:004293C6 mov cl, ah
:004293C8 and eax, FF
:004293CD shl edx, 08
:004293D0 movsx ecx, byte ptr [ecx+esi+220]
:004293D8 movsx eax, byte ptr [eax+esi+320]
:004293E0 or edx, ecx
:004293E2 pop esi
:004293E3 shl edx, 08
:004293E6 or edx, eax
:004293E8 mov eax, edx
:004293EA shl eax, 0B
:004293ED shr edx, 15
:004293F0 or eax, edx
:004293F2 ret 0004
Beutiful isn't it ? :)
A little explaintion on the decryption table is in place.
Breakpoint at CS:0042940A, and :D DS:ESI. The Table_Head is now at the Data window .
This is the strcture of the Decryption Table:
------------------------- - - -
[ESI] Table Head 20
------------------------- - -
[ESI+20] Decryption Tabel #1
100
------------------------- - -
[ESI+120] Decryption Tabel #2
100
------------------------- - - 420h
[ESI+220] Decryption Tabel #3
100
------------------------- - -
[ESI+320] Decryption Tabel #4
100
------------------------- - - -
As mentioned before , the Table_head is first the Constant_Set , that is used to
decrypt the first 20h bytes from File[0] Then these 20h decrypted bytes are copied as
the New_Table_Head , and it will be used to decrypt the other fields of the key-file.
There is a function that is called 32 times during the 8_bytes decryption. I call it
'The survival of the fittest' function .. Let's see why .
A 4byte number is pushed to the function : 03020671
Now each byte is used as INDEX, to get another byte from coresponding decryption table:
03 02 06 71
__ __ __ __
Input (EAX): |__|__|__|__|
A=Table#1 [03] = EE
B=Table#2 [02] = AE
C=Table#3 [06] = 26
D=Table#4 [71] = D2
Now for the rules of the jungle :
MovSx ecx, byte ptr [Table#X+Offset]
Or edx,ecx
This means that when a SIGNed number is loaded to ecx , he will wipe out the other
numbers in edx (becouse of the OR).
Example:
EDX=Tabel#1 [03] = FFFFFFEE (All the other 'F' is because of extended sign)
Now a new spices evolve.. but a strong property is not beeing able to run faster,
get more food or be wizer to build tools. A strong property is being a SIGNED
value..
ECX=Table#2 [02] = FFFFFFAE
(EDX is shifed left .. = FFFFEE00)
Now the struggle to survive.. If ecx is signed, will wipe out the old number, so
OR EDX,ECX
EDX=FFFFFFAE
Another spices evolve.. this time a weak one ..
ECX=Table#3 [06] = 00000026
(EDX is shifted left .. = FFFFAE00)
The big fight for teritory .. only this time ECX is not signed , so he'll have to
Share it ..
OR EDX,ECX
EDX=FFFFAE26
...........
After all this , who ever survives is being scrambled a bit , and this is the result
of the function.
It's nice to look at functions this way , isn't it ? ;)
Before we're going to reverse the protection algorithm , let's summerize :
- The main decryption routine decryptes 8_bytes each time .
- EBX and EDI gets those encrypted bytes (CS:0042940A).
- ESI points to the decryption table structure.
- The Table_Head is used to calculate the Input number for the Function.
- The Function uses the rest 4 decryption tables to calcualte result. It uses
the 'survival of the fittest' rules ;)
- The function is called 32 times in total.
- There is a pattern the repeats in the main decryption routine.
Now it's time to carefuly study the decryption, so we could build our own encrypter,
and create valid key-files.
Where to start ? Do we need to revers the decrypt_value function ? If the decryption
adds a value from the table_head to one of the encrypted bytes (ebx or edi), do we
need to use FSUB as reverse ? This is where i got lost the other time ..
Now however, we'll use a different techniqe , a more technical one.
Lets transtale the decryption procedure (CS:00429400) to a higher leng.
Buffer : Encrypted 8 bytes
Define : A = EDI
B = EBX
Table_head:Arry[1..8] of longint
X : a longint var.
This simple code :
:00429410 mov eax, dword ptr [esi+00]
:00429412 add eax, edi
:00429414 push eax
:00429415 call 004293A0
:0042941A mov edx, dword ptr [esi+04]
:0042941D xor ebx, eax
Will be transtaled as :
X:= A + Table_head[1];
B:= B Xor Func(X);
( Remember ESI points to table_head ? So dword ptr [ESI+0] is Table_head[1] ,
dword ptr [ESI+4] is Table_head[2] and so on.)
We continue translating this procedure , and see a clear pattern appears :
(CS:00429410)
A:= Buffer[0]; {EDI}
B:= Buffer[4]; {EBX}
X:= A + Table_head[1];
B:= B Xor Func(X);
X:= B + Table_head[2];
A:= A Xor Func(X);
X:= A + Table_head[3];
B:= B Xor Func(X);
X:= B + Table_head[4];
A:= A Xor Func(X);
X:= A + Table_head[5];
B:= B Xor Func(X);
X:= B + Table_head[6];
A:= A Xor Func(X);
X:= A + Table_head[7];
B:= B Xor Func(X);
X:= B + Table_head[8];
A:= A Xor Func(X);
X:= A + Table_head[8];
B:= B Xor Func(X);
X:= B + Table_head[7];
A:= A Xor Func(X);
.
.
.
X:= A + Table_head[2];
B:= B Xor Func(X);
X:= B + Table_head[1];
A:= A Xor Func(X);
Buffer[0]:=B;
Buffer[4]:=A;
(CS:00429628)
Now all there is left to do is reverse this routine :
X:= A + Table_head[1];
B:= B Xor Func(X);
X:= B + Table_head[2];
A:= A Xor Func(X);
But before we jump to it , lets look at some simple examples to reverse, and formaly
explain the rules to reverse , and what reversing means.
Example 1 :
A:=A+12;
B:=B xor A;
First , number the lines:
1. A:=A+12;
2. B:=B xor A;
Next add the get() and Print() to show exactly what is input, and what is output:
Get(A,B);
1. A:=A+12;
2. B:=B xor A;
Print(A,B);
Next change the Output vars to different names :
Get(A,B);
1. A^:=A+12;
2. B^:=B xor A^;
Print(A^,B^);
What we want to do when reversing :
__________
X | | Y
--> | Box | -->
Input | | Output
----------
__________
Y | Reversed | X
--> | Box | -->
Input | | Output
----------
Now to reverse it , go from the END of the routine . meaning first deal with
line #2 , and then #1.
Get(A^,B^); {It's now reversed ;) }
2. B^:=B Xor A^; ==> B:=B^ Xor A^;
1. A^:=A+12; ==> A:=A^-12;
Print(A,B);
And the revese is done =) This technique is a bit too technical .. but it works!
Example 2 :
X:=A ROL 2;
B:=B Xor X;
A:=Func(B) Xor X;
Step#1-Number lines
1. X:=A ROL 2;
2. B:=B Xor X;
3. A:=Func(B) Xor X;
Step#2-Show exactly what is input , and what is output
Get(A,B);
1. X:=A ROL 2;
2. B:=B Xor X;
3. A:=Func(B) Xor X;
Print(A,B);
Step#3-Deal with output as if they are different vars (change names)
Get(A,B);
1. X :=A ROL 2;
2. B^:=B Xor X;
3. A^:=Func(B^) Xor X;
Print(A^,B^);
Step#4-Minimum lines is better
Get(A,B);
1. B^:=B Xor (A ROL 2);
2. A^:=Func(B^) Xor (A ROL 2);
Print(A^,B^);
Step#5-Reverse
Get(A^,B^);
2. A^:=Func(B^) Xor (A ROL 2);
||
(A ROL 2)=A^ Xor Func(B^); ==> A:= ( A^ Xor Func(B^) ) ROR 2;
1. B^:=B Xor (A ROL 2); ==> B:= B^ Xor (A ROL 2);
Print(A,B);
As u can see, here we didn't have to reverse Func(X).. no need to build Un-Func(X)..
Reversing The Decrypion
Ok , lets reverse :
X:= A + Table_head[1];
B:= B Xor Func(X);
X:= B + Table_head[2];
A:= A Xor Func(X);
----------------------------
Get(A,B);
1. X:= A + Table_head[1];
2. B:= B Xor Func(X);
3. X:= B + Table_head[2];
4. A:= A Xor Func(X);
Print(A,B);
----------------------------
Get(A,B);
1. X := A + Table_head[1];
2. B^:= B Xor Func(X);
3. X := B^ + Table_head[2];
4. A^:= A Xor Func(X);
Print(A^,B^);
----------------------------
Get(A,B);
1. B^:= B Xor Func(A + Table_head[1]);
2. A^:= A Xor Func(B^+ Table_head[2]);
Print(A^,B^);
----------------------------
Get(A^,B^);
2. A^:= A Xor Func(B^+ Table_head[2]); ==> A:= A^ Xor Func(B^+ Table_head[2]);
1. B^:= B Xor Func(A + Table_head[1]); ==> B:= B^ Xor Func(A + Table_head[1]);
Print(A,B);
That's It =)
Now the encrypter will be this pattern , appeared backwords than in the decryption:
B:= Buffer[0];
A:= Buffer[4];
A:= A Xor Func(B + Table_head[1]);
B:= B Xor Func(A + Table_head[2]);
A:= A Xor Func(B + Table_head[3]);
B:= B Xor Func(A + Table_head[4]);
A:= A Xor Func(B + Table_head[5]);
B:= B Xor Func(A + Table_head[6]);
.
.
.
A:= A Xor Func(B + Table_head[2]);
B:= B Xor Func(A + Table_head[1]);
Buffer[0]:=A;
Buffer[4]:=B;
(As u see, we don't need to reverse 'the survival of the fittest' function...
Leave nature alone ;)
After reversing the decryption, we now have the encryption procedure to create our
valid key-files. Building a key-maker would be a matter of minutes of course,
yet there's absolutely no need at all: there are infact
already
a series of keygenerators out there on the warez world,
the best one being Phrozen Crew's PC_CUT2X.ZIP.
As always , I searched for other parts in the target to study, and i found
that cuteftp encryptes the password for the ftps u put on the list. After a simple
look with HexEditor on TREE.DAT u see the password is encypted by a simple XOR.
Very disapointing after after having seen the though protection for the key-file.
(Oh well , it's no wonder the programmer doesn't give much when it comes to
protect YOUR files..)
Anyway , i hope someone has learned from this.
Feel free to ask me anything (but crack-requests): Phrozen_q(at)CyberDude(point)Com
Greetings to all PC members, Fravia+ and all the crackers in the world =)
The+Q
I wont even bother explaining you
that you should BUY this target program if you intend to use it for a longer
period than the allowed one. Should you want
to STEAL this software instead, you don't need to crack its protection
scheme at all: you'll
find it on most Warez sites, complete and already regged, farewell.
You are deep inside fravia's page of reverse engineering,
choose your way out:
homepage
links
search_forms
+ORC
students' essays
academy database
reality cracking
how to search
javascript wars
tools
anonymity academy
cocktails
antismut CGI-scripts
mail_fravia+
Is reverse engineering legal?