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


                      ͻ
                        Tutorial for B.E.S.T crackme 
                      ͼ


Best viewed with NFO viewer

Ŀ
1.Intro 


This crackme was published on Fravia's site the 12 november 1998 :  I will let you read the
'newchaosprot.htm' file to have a full description of the target.

When I first tried this crackme 2 years agos, I didn't manage to solve it ... now it's time for
"TSCube's revenge" ;)

To solve this crackme, we must :
1. Find the method used for protection, and describe this method as specifically as possible
2. Find the exact registration key that must be used
3. Create a "crippled" version that circumvents any need for a registration key

I won't cover part 3 because when you got the correct key, you know how to patch.

note : This a definitively not a tutorial for newbies



Ŀ
2. Description of the method used for protection 


As you can see, there is a 'prot.dll' file where the protection scheme functions are stored, and
a keyfile 'key.reg'.

I don't want to spend to much time explaining how I managed to understand the method used for
protection... I will rather explain how the protection itself works.


	2.1. The functions of 'prot.dll'
	

Here are the function of the dll :

Addr:10001423 Ord:   1 (0001h) Name: _AddFunc
Addr:1000177E Ord:   2 (0002h) Name: _DestroyFuncs
Addr:10001794 Ord:   3 (0003h) Name: _SetDefaultFunc
Addr:10001501 Ord:   4 (0004h) Name: _SetRegistrationKey
Addr:1000151C Ord:   5 (0005h) Name: _SetSerialNo
Addr:100016F5 Ord:   6 (0006h) Name: _pGetFuncbyCode


		2.1.1. _SetRegistrationKey
		

This function is called once during the crackme initialisation (@4025A4) : it reads 16 bytes from
'reg.key' and stores them in an array located at @10002000.

These bytes are then used during the _pGetFuncbyCode function

<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->
Exported fn(): _SetRegistrationKey - Ord:0004h
:10001501 56                      push esi
:10001502 8B742408                mov esi, dword ptr [esp + 08] -> here are the 16 bytes 
                                                                   from 'reg.key'
:10001506 31C9                    xor ecx, ecx

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:10001518(C)
|
:10001508 89F0                    mov eax, esi
:1000150A 46                      inc esi
:1000150B 8A00                    mov al, byte ptr [eax]
:1000150D 88040D00200010          mov byte ptr [ecx + 10002000], al -> bytes are stored there
:10001514 41                      inc ecx
:10001515 83F910                  cmp ecx, 00000010
:10001518 7CEE                    jl 10001508
:1000151A 5E                      pop esi
:1000151B C3                      ret
<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->


		2.1.2. _SetSerialNo
		

This function is called once during the crackme initialisation (@4025B8) : it stores 16 bytes
in an array located at @10002000

In this crackme, the 16 bytes of the serial_no are hardcoded :

unsigned char regno[16] = { 0x55,0x89,0xE5,0x81,0xEC,0x54,0x06,0x00,
                          0x00,0xB9,0x95,0x01,0x00,0x00,0x49,0xC7
	                   };

As the author says, in a *real* protection, these 16 bytes would be different for each computer.

<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->
Exported fn(): _SetSerialNo - Ord:0005h
:1000151C 56                      push esi
:1000151D 8B742408                mov esi, dword ptr [esp + 08] -> here are the 16 (hardcoded)
                                                                   bytes
:10001521 31C9                    xor ecx, ecx

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:10001533(C)
|
:10001523 89F0                    mov eax, esi
:10001525 46                      inc esi
:10001526 8A00                    mov al, byte ptr [eax]
:10001528 88040D10200010          mov byte ptr [ecx + 10002010], al -> bytes are stored there
:1000152F 41                      inc ecx
:10001530 83F910                  cmp ecx, 00000010
:10001533 7CEE                    jl 10001523
:10001535 5E                      pop esi
:10001536 C3                      ret
<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->


		2.1.3. _SetDefaultFunc
		

This function is called once during the crackme initialisation (@4025C5). Here is the call :

<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->
:004025C0 68A4284000              push 004028A4 -> @ of default "unregistered user" function

* Reference To: prot._SetDefaultFunc, Ord:0003h
                                  |
:004025C5 E8620F0000              Call 0040352C
<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->

_SetDefaultFunc is used to store the @ of the function that will be called if the users tries
to use a feature of the registered proggy (i.e : the default function will be called if the
keyfile is NOT valid).
Let's have a look at this "unregistered user" function :

<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->
:004028A4 6A40                    push 00000040
:004028A6 6840624000              push 00406240

* Possible StringData Ref from Data Obj ->"You must register this product!"
                                  |
:004028AB 6854624000              push 00406254
:004028B0 FF35A8414000            push dword ptr [004041A8]

* Reference To: USER32.MessageBoxA, Ord:018Ah
                                  |
:004028B6 E839100000              Call 004038F4
:004028BB C3                      ret
<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->

As you can see, it simply displays a nagscreen


		2.1.4. _AddFunc
		

This function is called 5 times during the crackme initialisation :

<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->
:004025CD 68B0184000              push 004018B0 -> @ of 'encryption' function

* Reference To: prot._AddFunc, Ord:0001h
                                  |
:004025D2 E8490F0000              Call 00403520
:004025D7 83C404                  add esp, 00000004
:004025DA 68C41C4000              push 00401CC4 -> @ of 'set encryption key' function

* Reference To: prot._AddFunc, Ord:0001h
                                  |
:004025DF E83C0F0000              Call 00403520
:004025E4 83C404                  add esp, 00000004
:004025E7 68BA1A4000              push 00401ABA -> @ of 'decryption' function

* Reference To: prot._AddFunc, Ord:0001h
                                  |
:004025EC E82F0F0000              Call 00403520
:004025F1 83C404                  add esp, 00000004
:004025F4 68C2244000              push 004024C2 -> @ of 'set key' function

* Reference To: prot._AddFunc, Ord:0001h
                                  |
:004025F9 E8220F0000              Call 00403520
:004025FE 83C404                  add esp, 00000004
:00402601 684F1E4000              push 00401E4F -> @ of 'enable decryption menuitem' function

* Reference To: prot._AddFunc, Ord:0001h
                                  |
:00402606 E8150F0000              Call 00403520
<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->

_AddFunc is used to store the @ of the functions which must not be used by a non_registered 
user : these functions are, in a certain way, "protected" by the protection scheme.

Here the crackme protects 5 functions, but only 2 of them ('enable decryption menuitem' and
''decryption') really need to be protected. In fact, the author decided to protect them all
to help the cracking process ;)

If you really want to know, the @ of these functions are stored in a sort of "chained-list".


		2.1.5. _DestroyFuncs
		

This function is not called in the crackme, but it's supposed to free the memory allocated by
_AddFunc to store the @ of the functions.


		2.1.6. _pGetFuncbyCode
		

Here is the core (hi Duelist ;) of the protection scheme.

Each function protected by _AddFunc is associated with a number (Function_Code). If a user
wants to use a function protected by _AddFunc, the crackme calls _pGetFuncbyCode using the
Function_Code as a parameter. 
If the user is registered, _pGetFuncbyCode returns the @ of the function associated to the
Function_Code, if the user is not registered, _pGetFuncbyCode returns the @ of the function 
stored with _SetDefaultFunc.

Let's take an example : as you can see, the 'Decrypt' function can not be used by an unregistered
user because the menu item is grayed. To be able to use it, the 'enable decryption menuitem'
function must be called (@401E4F). 
The function_Code of the 'enable decryption menuitem' function is 0x75564714, and we're supposed
to call this function just after the 'Set key' function is called :

<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->
We land here if users click on 'Set key'
----------------------------------------

:00401D81 57                      push edi

* Possible Reference to String Resource ID=06000: "Select file to use as encryption key"
                                  |
:00401D82 6870170000              push 00001770
:00401D87 FF359C414000            push dword ptr [0040419C]

[...]

* Reference To: USER32.GetMenu, Ord:0103h
                                  |
:00401DFF E83C1A0000              Call 00403840
:00401E04 8985B0FCFFFF            mov dword ptr [ebp+FFFFFCB0], eax
:00401E0A 6A00                    push 00000000
:00401E0C 68C8000000              push 000000C8
:00401E11 FFB5B0FCFFFF            push dword ptr [ebp+FFFFFCB0]

* Reference To: USER32.EnableMenuItem, Ord:00AAh
                                  |
:00401E17 E8C4190000              Call 004037E0 -> enabled 'encrypt' menu item
:00401E1C 6814475675              push 75564714 -> Function_Code of 'enable decryption menuitem'

* Reference To: prot._pGetFuncbyCode, Ord:0006h
                                  |
:00401E21 E82A170000              Call 00403550 -> get @ of this function
:00401E26 83C404                  add esp, 00000004
:00401E29 8985ACFBFFFF            mov dword ptr [ebp+FFFFFBAC], eax 
:00401E2F FF95ACFBFFFF            call dword ptr [ebp+FFFFFBAC] -> call function
:00401E35 6A40                    push 00000040
:00401E37 6810614000              push 00406110

* Possible StringData Ref from Data Obj ->"Key successfully set!"
                                  |
:00401E3C 6811614000              push 00406111
:00401E41 FF35A8414000            push dword ptr [004041A8]

* Reference To: USER32.MessageBoxA, Ord:018Ah
                                  |
:00401E47 E8A81A0000              Call 004038F4
<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->

Well, I hope you got it now : if user is registered, the 'Decrypt' menu item will be enabled.
If user if not registered the nagscreen will be displayed

It's not very important to understand what is the relation between the Function_Code and the
associated function.


	2.2. Study of _pGetFuncbyCode
	

<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->
Exported fn(): _pGetFuncbyCode - Ord:0006h
:100016F5 55                      push ebp
:100016F6 89E5                    mov ebp, esp
:100016F8 53                      push ebx
:100016F9 56                      push esi
:100016FA 57                      push edi
:100016FB 8B7508                  mov esi, dword ptr [ebp+08] -> Function_Code

:100016FE 6810200010              push 10002010 -> SerialNo (see _SetSerialNo)
:10001703 6800200010              push 10002000 -> RegistrationKey (see _SetRegistrationKey)
:10001708 E82AFEFFFF              call 10001537

This function uses the SerialNo and the RegistrationKey to generate a magic number

:1000170D 31C6                    xor esi, eax -> ESI = Function_Code ^ Magic_number
:1000170F 56                      push esi
:10001710 E8C1FDFFFF              call 100014D6

This function returns the @ of the function associated to (Function_Code ^ Magic_number) or 0 if
user is not registered

:10001715 83C40C                  add esp, 0000000C
:10001718 89C7                    mov edi, eax
:1000171A 09FF                    or edi, edi
:1000171C 7404                    je 10001722 -> NOT registered
:1000171E 89F8                    mov eax, edi
:10001720 EB28                    jmp 1000174A -> registered

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:1000171C(C)
|
:10001722 833D2020001000          cmp dword ptr [10002020], 00000000
:10001729 7407                    je 10001732
:1000172B A120200010              mov eax, [10002020] -> @ of default function 
                                                         (see _SetDefaultFunc)
:10001730 EB18                    jmp 1000174A

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:10001729(C)
|
:10001732 8D3DF5160010            lea edi, dword ptr [100016F5]

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:10001746(C)
|
:10001738 89F8                    mov eax, edi
:1000173A 47                      inc edi
:1000173B 8A10                    mov dl, byte ptr [eax]
:1000173D 0FBECA                  movsx ecx, dl
:10001740 41                      inc ecx
:10001741 8808                    mov byte ptr [eax], cl
:10001743 80FA00                  cmp dl, 00
:10001746 75F0                    jne 10001738
:10001748 31C0                    xor eax, eax

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:10001720(U), :10001730(U)
|
:1000174A 5F                      pop edi
:1000174B 5E                      pop esi
:1000174C 5B                      pop ebx
:1000174D 5D                      pop ebp
:1000174E C3                      ret
<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->


		2.2.1. call 100014D6
		

Here is what I said earlier :

:1000170D 31C6                    xor esi, eax -> ESI = Function_Code ^ Magic_number
:1000170F 56                      push esi
:10001710 E8C1FDFFFF              call 100014D6

This function returns the @ of the function associated to (Function_Code ^ Magic_number) or 0 if
user is not registered :

<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->
:100014D6 56                      push esi
:100014D7 8B742408                mov esi, dword ptr [esp + 08] -> Function_Code ^ Magic_number
:100014DB 8B0D24200010            mov ecx, dword ptr [10002024] -> see _AddFunc
:100014E1 EB16                    jmp 100014F9

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:100014FB(C)
|
:100014E3 397104                  cmp dword ptr [ecx+04], esi
:100014E6 7504                    jne 100014EC
:100014E8 8B01                    mov eax, dword ptr [ecx]
:100014EA EB13                    jmp 100014FF -> registered

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:100014E6(C)
|
:100014EC 397104                  cmp dword ptr [ecx+04], esi
:100014EF 7D05                    jge 100014F6
:100014F1 8B4908                  mov ecx, dword ptr [ecx+08]
:100014F4 EB03                    jmp 100014F9

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:100014EF(C)
|
:100014F6 8B490C                  mov ecx, dword ptr [ecx+0C]

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:100014E1(U), :100014F4(U)
|
:100014F9 09C9                    or ecx, ecx
:100014FB 75E6                    jne 100014E3
:100014FD 31C0                    xor eax, eax -> fuck off

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:100014EA(U)
|
:100014FF 5E                      pop esi
:10001500 C3                      ret
<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->

Oh my god, how I am going to explain what's happening here ????? ;)

As I said before, _AddFunc stores the @ of the functions in a sort "chained_list"... and here
is the chained list used by this crackme :


Ŀ
 0x4018B0   
Ĵ
   0x2B     
Ĵ      Ŀ
     *>   0x401CC4  
Ĵ      Ĵ
     *                0x99    
      Ĵ      Ŀ
                        *>  0x401ABA   
                   Ĵ      Ĵ
                        *                0xE5    
                         Ĵ
                                           *      
                                      Ĵ
                                                  
Ŀ      Ŀ       
  0x401E4F          0x4024C2  
Ĵ      Ĵ
   0x15               0x3D    
Ĵ      Ĵ
                              
Ĵ      Ĵ
                              
      

If you've never used chained_lists, each element is a 4 fields structure containing datas and
pointers to others structures :

Ŀ
  @ of fct   First field
Ĵ 
   number    Second field (it's not necessary to know how this number is computed)
Ĵ
  Pointer1   Third field
Ĵ
  Pointer1   Fourth field


The above function goes through the chained list and looks for a structure containing in its
second field the same value that ESI. If it finds it, it returns the value of the first field
(@ of fct), else it returns 0.

During the first call to _pGetFuncbyCode, the return value is supposed to be 0x404E4F (@ of 
'enable decryption menuitem' function), that means that ESI is supposed to be equal to 0x15.

Since ESI = Function_Code ^ Magic_number , this means that :
Magic_number =  ESI ^ Function_Code = 0x15 ^ 0x75564714 = 0x75564701 



Ŀ
3. Find the exact registration key that must be used


I said that :

:100016FE 6810200010              push 10002010 -> SerialNo (see _SetSerialNo)
:10001703 6800200010              push 10002000 -> RegistrationKey (see _SetRegistrationKey)
:10001708 E82AFEFFFF              call 10001537

This function uses the SerialNo and the RegistrationKey to generate a magic number

AND now we know that 'Magic_number = 0x75564701', so we all we have to do is study in deep
this 'call 10001537' and create a 'key.reg' file which will generate a Magic_number equal to
0x75564701.

Hum... should I say it ? I don't know... OK, here is the thing : the 'call 10001537' uses FPU
arithmetic to generate the Magic_number.

To be honnest, I must say I spent a few hours on this call because I had never studied such
FPU protections before... then I realised it's possible to create the keyfile without
knowing anything about FPU. At least I have learnt something ;) 

Here is the end of this function :

<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->
'dword ptr [ebp+08]' points to the last 4 letters of the RegistrationKey ("egCd")
'EBX' points to the last 4 values of the SerialNo (0x00,0x00,0x49,0xC7)

The following function XORES the last 4 letters of the RegistrationKey with the last 4 values
of the SerialNo :

:100016B7 8B4508                  mov eax, dword ptr [ebp+08] <---------+                                                               
:100016BA FF4508                  inc [ebp+08]                          |
:100016BD 89DA                    mov edx, ebx                          |
:100016BF 43                      inc ebx                               |
:100016C0 0FBE00                  movsx eax, byte ptr [eax]             | Final_loop
:100016C3 0FBE12                  movsx edx, byte ptr [edx]             |
:100016C6 31D0                    xor eax, edx                          |
:100016C8 884435F4                mov byte ptr [ebp + esi - 0C], al     |
:100016CC 46                      inc esi                               |
:100016CD 83FE04                  cmp esi, 00000004                     |
:100016D0 7CE5                    jl 100016B7 --------------------------+

[SNIP]

[EBP-20] is a FPU_number generated by the first part of the function. it depends on the
first 12 letters of the keyfile

:100016E8 8B45F4                  mov eax, dword ptr [ebp-0C] -> Keyfile_number computed in
                                                                 Final_loop
:100016EB 3145E0                  xor dword ptr [ebp-20], eax

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:100016E6(U)
|
:100016EE 8B45E0                  mov eax, dword ptr [ebp-20] -> Magic_number (must be equal to
                                                                 0x0x75564701)
:100016F1 5E                      pop esi
:100016F2 5B                      pop ebx
:100016F3 C9                      leave
:100016F4 C3                      ret
<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->

To summarize : 'Magic_number = FPU_number ^ Keyfile_number'

If you use SICE, you'll see that FPU_number = 0xBD733A78

=> Keyfile_number = 0xBD733A78 ^ 0x75564701 = 0xC8257D79

Here are the equations to solve :

{ regno[12] ^ keyfile[12] = 0x79
{ regno[13] ^ keyfile[13] = 0x7D
{ regno[14] ^ keyfile[14] = 0x25
{ regno[15] ^ keyfile[15] = 0xC8

{ keyfile[12] = regno[12] ^ 0x79 = 0x00 ^ 0x79 = 0x79 ('y')
{ keyfile[13] = regno[13] ^ 0x7D = 0x00 ^ 0x7D = 0x7D ('}')
{ keyfile[14] = regno[14] ^ 0x25 = 0x49 ^ 0x25 = 0x6C ('l')
{ keyfile[15] = regno[15] ^ 0xC8 = 0xC7 ^ 0xC8 = 0x0F (non printable)

Here are the bytes of the keyfile :

 0x44 0x65 0x6D 0x6F 0x56 0x65 0x72 0x73 0x69 0x6F 0x6E 0x52 0x79 0x7D 0x6C 0x0F 0x0D 0x0A

          First 12 bytes (unchanged)                          Last 4 bytes       Carriage return
                                           


Ŀ
4. Outro


Fiewwww... long tutorial, but I don't care because I took my revenge ;)

    ________     _______     _______
   /__   __/\   /  ____/\   /  ____/\
   \_/  /\_\/  /  /\___\/  /  /\___\/
    /  / /    /  /_/_     /  / / 
   /  / /    /____  /\   /  / /
  /  / /     \___/ / /  /  / /
 /  / /     ____/ / /  /  /_/_
/  / /     /_____/ /  /______/\
\__\/      \_____\/   \______\/ 23/07/2000

www.tscube.cjb.net