	  Ŀ
	                                                                 Ŀ
	                             
	                  
	                              
	                                 
	                             
	                                      
	                                                
	                                           
	                                        
	                                 
	                                 
	                                   
	                                     
	                                  
	                                                      
	                                                                     
	                            
	                  p r o u d l y     p r e s e n t s                  
	                            
	Ŀ                                              www.tscube.cjb.net 
	  


			Tutorial for Phrozen Crew crackme #2
			------------------------------------




+----------+
| 1. Intro |
+----------+

This is my first tutorial for a WIN32ASM crackme. These crackmes are always interesting because :

1) they are small ( less that 10 ko ... much better that most 200ko Delphi crackmes ;)
2) the protection scheme is not hidden with useless garbage code
3) the protection scheme is often worth reversing

YES, I know this crackme is pretty old, but it's part of my revenge on PC crackmes ;)




+----------------------------------------+
| 2. Another journey in the dead listing |
+----------------------------------------+

As usual : enter your name and any serial, 'BPX HMEMCPY', type any letter, SICE pops,
disable breakpoints, then F12 until you land here :

	2.1. Main function
	==================

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004012EC(C)
|
:004012F7 6A00                    push 00000000
:004012F9 6A00                    push 00000000
:004012FB 6A66                    push 00000066
:004012FD FF7508                  push [ebp+08]

* Reference To: USER32.GetDlgItemInt, Ord:0000h
                                  |
:00401300 E821010000              Call 00401426
:00401305 A3D2214000              mov dword ptr [004021D2], eax
:0040130A 3C00                    cmp al, 00
:0040130C 743F                    je 0040134D
:0040130E 90                      nop
:0040130F 90                      nop
:00401310 90                      nop
:00401311 90                      nop
:00401312 E85E000000              call 00401375 -> first interesting function
:00401317 E8A5000000              call 004013C1 -> second interesting function
:0040131C 85C0                    test eax, eax
:0040131E 742D                    je 0040134D -> fuck off
:00401320 90                      nop
:00401321 90                      nop
:00401322 90                      nop
:00401323 90                      nop
:00401324 6A00                    push 00000000

* Possible StringData Ref from Data Obj ->"Correct !"
                                  |
:00401326 689F214000              push 0040219F




<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->
<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->



	2.2. first interesting function
	===============================

* Referenced by a CALL at Address:
|:00401312   
|
:00401375 33C9                    xor ecx, ecx
:00401377 BEA9214000              mov esi, 004021A9

ESI points to our name, but if you look better, you'll notice that a few numbers are added
to the end of this name if it's not long enough :

"TSCube" -> "TSCube01" (total length = 8)
"TSC" -> "TSC01234" (total length = 8)
"T" -> "T0123456" (total length = 8)

in 'C', it looks like => strncat((char*)buffer,"01234567",8-strlen(szname));


:0040137C 8BFE                    mov edi, esi

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401391(C)

begin_loop_1
------------

:0040137E AC                      lodsb
:0040137F F7D1                    not ecx
:00401381 8A99B1214000            mov bl, byte ptr [ecx+004021B1]
:00401387 F7D1                    not ecx
:00401389 02C3                    add al, bl
:0040138B AA                      stosb
:0040138C FEC1                    inc cl
:0040138E 80F904                  cmp cl, 04
:00401391 75EB                    jne 0040137E

end_loop_1
----------

this loop modifies the first 4 letters of your name (now you understand why the name had to be
long enough) :

name[0] = name[0] + name[7];
name[1] = name[1] + name[6];
name[2] = name[2] + name[5];
name[3] = name[3] + name[4];



:00401393 B904000000              mov ecx, 00000004
:00401398 BEA9214000              mov esi, 004021A9 -> modified name
:0040139D 8BFE                    mov edi, esi
:0040139F 83C704                  add edi, 00000004
:004013A2 F2                      repnz
:004013A3 A4                      movsb
:004013A4 BEA9214000              mov esi, 004021A9
:004013A9 8BFE                    mov edi, esi
:004013AB B908000000              mov ecx, 00000008

the above lines are equivalent to :

for (unsigned int i=0;i<4;i++) name[i+4] = name[i];


begin_loop_2
------------

We loop 8 times

:004013B0 AC                      lodsb
:004013B1 F7D1                    not ecx
:004013B3 8A99DF214000            mov bl, byte ptr [ecx+004021DF] -> "PhroZenQ" (magic string)
:004013B9 F7D1                    not ecx
:004013BB 32C3                    xor al, bl
:004013BD AA                      stosb
:004013BE E2F0                    loop 004013B0

end_loop_2
-----------

This is equivalent to :

for (i=0;i<8;i++) name[i] = name[i] ^ magic[i];


:004013C0 C3                      ret



<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->
<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->



	2.3. Second interesting function
	================================

This function uses the modified_name at @4021A9 to compute 2 magic values, which will be
compared at @4013E0.

name -> "TSCube"
modified_name (@4021A9) -> {0xD5,0xEB,0xDA,0xB8,0xDF,0xE6,0xC6,0x86,0x00}

From now, we will represent this array like this : { A B C D E F G H I }
for "TSCube", we got A=0xD5 B=0xEB etc...


* Referenced by a CALL at Address:
|:00401317   

:004013C1 A1D2214000              mov eax, dword ptr [004021D2]

EAX contains the hexa value of the serial (GETDLGITEMINT)

:004013C6 8A0DA9214000            mov cl, byte ptr [004021A9] -> A
:004013CC D3C0                    rol eax, cl
:004013CE 8B0DAE214000            mov ecx, dword ptr [004021AE] -> IHGF (0x0086C6E6 for my name)
:004013D4 33C1                    xor eax, ecx
:004013D6 8B1DAA214000            mov ebx, dword ptr [004021AA] -> EDCB (0xDFB8DAEB for my name)
:004013DC 8AC8                    mov cl, al
:004013DE D3CB                    ror ebx, cl
:004013E0 33C3                    xor eax, ebx -> EAX and EBX must be equal

that means that :

'(serial ROL A) ^ IHGF' must be equal to 'EDCB ROR (((serial ROL A) ^ IHGF) & 0xFF)'

:004013E2 B800000000              mov eax, 00000000
:004013E7 7506                    jne 004013EF -> fuck off
:004013E9 90                      nop
:004013EA 90                      nop
:004013EB 90                      nop
:004013EC 90                      nop
:004013ED FEC0                    inc al -> REGISTERED !!!

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004013E7(C)
|
:004013EF C3                      ret




+--------------------------+
| 3. Let's take an example |
+--------------------------+

for my name "TSCube", the correct serial is 3973902268

The magic array generated in the first function is : {D5 EB DA B8 DF E6 C6 86 00}

1) '(serial ROL A) ^ IHGF' <=> (3973902268 ROL 0xD5) ^ 0x86C6E6DF = 0xF71B5D7B
2) 'EDCB ROR (((serial ROL A) ^ IHGF) & 0xFF)' <=> 0xDFB8DAEB ROR (0xF71B5D7B & 0xFF)
                                               <=> 0xDFB8DAEB ROR 0x7B
                                               <=> 0xF71B5D7B

both equations are equal, so the serial is good




+----------------------+
| 4. A useful function |
+----------------------+

If you're like me, and you like to do all your keygens in 'C', you'll need a special function 
to make a keygen. (to be more precise, I don't do exactly my keygens in 'C' because I use some 
of the advantages from 'C++', but I don't dare calling it 'C++'... and so should you AB ;)

Look at this line :

:004013D6 8B1DAA214000            mov ebx, dword ptr [004021AA] -> EDCB (0xDFB8DAEB for my name)

If you remember well, our array containing the values A...I is located at @4021A9.
So, this ASM line converts to a DWORD value the 4 values : array[1],array[2],array[3],array[4]

This can't be done so easily in 'C', so we have to write a little function :

unsigned int chars2dword(unsigned char* array)
{
	unsigned int dword = 0;
	
	for (int i=3;i>=0;i--) 
	{
		dword = dword << 8;
		dword = dword | (int)array[i];
	}

	return dword;
}


This function returns the DWORD value corresponding to the 4 first values of the array passed
as argument.




+--------------------------+
| 5. Let's make a keygen   |
+--------------------------+

We got this equation to solve :

(serial ROLxA) ^ IHGF = EDCB ROR ( ((serial ROL A) ^ IHGF) & 0xFF)

Remember that the values A...I are generated from the name, so we'll just have to reproduce
the behavior of the first function (@401375) to find them.


	5.1. First brute force approach
	===============================

Well, this solution is pretty direct : we can simply try all the possible values of the serial
(2^32 possibilities).

unsigned int dword1 = chars2dword(&buffer[5]);
unsigned int dword2 = chars2dword(&buffer[1]);

for (unsigned int brute=0;brute<0xFFFFFFFF;brute++)
{
	unsigned int result1,result2;

	result1 = _rotl(brute,buffer[0]);
	result1 = result1 ^ dword1;
	result2 = _rotr(dword2,result1 & 0xFF);
		
	if (result1==result2) { DISPLAY THE 'BRUTE' VALUE }
}



	5.2. Second brute force approach
	================================

If we think a little (come on, you can do it ! ;), we notice that in fact we only got to
solve this :

X = EDCB ROR (X & 0xFF) ; with X = (serial ROLxA) ^ IHGF

If we know X, we can easily find the serial  : serial = (X ^ IHGF) ROR A


unsigned int dword1 = chars2dword(&buffer[5]);
unsigned int dword2 = chars2dword(&buffer[1]);

for (unsigned int brute=0;brute<0xFFFFFFFF;brute++)
{
	if (_rotl(brute,brute & 0xFF) == dword2)
	{
		unsigned int serial = _rotr(brute ^ dword1,buffer[0]);
		... // DISPLAY 'SERIAL' VALUE
	}
}



	5.3. Third brute force approach
	===============================

Damn, the 2 above bruteforce methods are pretty slow... can't it be faster ? YES, it can !

Let's say X= 0x12345678 : X = EDCB ROR (X & 0xFF) <=> 0x12345678  = EDCB ROR 0x78

We'll try ALL the possible values of the lowest byte of X (2^8 possibilities) and then we'll
check if (EDCB ROR brute) & 0xFF = brute !!!



unsigned int dword1 = chars2dword(&buffer[5]);
unsigned int dword2 = chars2dword(&buffer[1]);

for (unsigned int brute=0;brute<256;brute++)
{
	unsigned int temp = _rotr(dword2,brute); 
	if ( (temp & 0xFF) == brute)
	{
		unsigned int serial = _rotr(temp ^ dword1,buffer[0]);
		... // DISPLAY 'SERIAL' VALUE
	}
}



This method is pretty fast because you have only 255 values to try.
Anyway, it's not possible to find a serial for every name you might type, but who cares ? ;)




+----------+
| 6. Outro |
+----------+

Woaahh, the size of this tutorial is bigger that the size if the crackme (I don't even speak
of the keygen)... maybe I should write my tuts in ASM ? ;)

   
    ________     _______     _______
   /__   __/\   /  ____/\   /  ____/\
   \_/  /\_\/  /  /\___\/  /  /\___\/
    /  / /    /  /_/_     /  / / 
   /  / /    /____  /\   /  / /
  /  / /     \___/ / /  /  / /
 /  / /     ____/ / /  /  /_/_
/  / /     /_____/ /  /______/\
\__\/      \_____\/   \______\/ 26/06/2000

www.tscube.cjb.net