			Tutorial for Falcon's 7th Crackme
			---------------------------------

By TSCube
---------


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

This crackme involves math/logic stuff, so if math makes you want to bump your head against the
wall, don't even try this crackme.


+----------------------------------------+
| 2. Understanding the protection scheme |
+----------------------------------------+

Enter any serial, 'BPX HMEMCPY', then click on "Check it" -> SICE POPS.
Now disable the breakpoint, hit F12 until you're back in the main code of the crackme and
trace with F10 : 

:00456976 8B45FC                  mov eax, dword ptr [ebp-04] -> entered_serial
:00456979 E8FED1FAFF              call 00403B7C -> strlen
:0045697E 83F804                  cmp eax, 00000004 -> The serial must have at least 5 digits
:00456981 7E3D                    jle 004569C0

:00456983 803D2498450000          cmp byte ptr [00459824], 00
:0045698A 7514                    jne 004569A0 -> jump if first serial is correct

:0045698C 8B45FC                  mov eax, dword ptr [ebp-04] -> entered_serial
:0045698F E868FFFFFF              call 004568FC -> serial check routine #1
:00456994 83F902                  cmp ecx, 00000002
:00456997 7507                    jne 004569A0

:00456999 C6052498450001          mov byte ptr [00459824], 01 -> first serial is correct

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0045698A(C), :00456997(C)
|
:004569A0 FE0525984500            inc byte ptr [00459825]
:004569A6 803D2498450001          cmp byte ptr [00459824], 01
:004569AD 7511                    jne 004569C0 -> fuck off if first serial is not correct

:004569AF 803D2598450002          cmp byte ptr [00459825], 02
:004569B6 7508                    jne 004569C0

:004569B8 8B45FC                  mov eax, dword ptr [ebp-04] -> entered_serial
:004569BB E8FCFEFFFF              call 004568BC -> serial check routine #2

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00456981(C), :004569AD(C), :004569B6(C)
|
:004569C0 33C0                    xor eax, eax
:004569C2 5A                      pop edx
:004569C3 59                      pop ecx
:004569C4 59                      pop ecx
:004569C5 648910                  mov dword ptr fs:[eax], edx
:004569C8 68DD694500              push 004569DD

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004569DB(U)
|
:004569CD 8D45FC                  lea eax, dword ptr [ebp-04]
:004569D0 E827CFFAFF              call 004038FC
:004569D5 C3                      ret



The above listing uses 2 values stored at [00459824] and [00459825]. In case you have not noticed
it yet, the user must enter *2* different serials... the algo works like this :

IF first_serial is not valid
	check first_serial
END IF

IF first_serial is valid
	IF it's the second time user clicks on "Check it"
		check_second_serial and display "registered" if it's correct
	END IF
END IF


+-----------------------------+
| 3. Finding the first serial |
+-----------------------------+

The first serial check routine is at @4568FC. When the proggy returns from this function,
ECX=2 if the first serial is correct

* Referenced by a CALL at Address:
|:0045698F   
|
:004568FC 55                      push ebp
:004568FD 31C9                    xor ecx, ecx
:004568FF 8D30                    lea esi, dword ptr [eax]
:00456901 83C604                  add esi, 00000004
:00456904 BB998F3337              mov ebx, 37338F99              }
:00456909 AD                      lodsd                          }
:0045690A 31C3                    xor ebx, eax                   }
:0045690C C1C330                  rol ebx, 30                    }
:0045690F 81F300009999            xor ebx, 99990000              }
:00456915 C1CB50                  ror ebx, 50                    } FIRST PART OF CHECK ROUTINE
:00456918 83EE08                  sub esi, 00000008              }
:0045691B AD                      lodsd                          }
:0045691C 01C3                    add ebx, eax                   }
:0045691E 81F399999999            xor ebx, 99999999              }
:00456924 C1C370                  rol ebx, 70                    }
:00456927 81FB75533D53            cmp ebx, 533D5375              }
:0045692D 751C                    jne 0045694B -> fuck off

:0045692F 41                      inc ecx

:00456930 83EE04                  sub esi, 00000004
:00456933 AD                      lodsd                         }
:00456934 89C3                    mov ebx, eax                  }
:00456936 AD                      lodsd                         }
:00456937 C1CB80                  ror ebx, 80                   } SECOND PART OF CHECK ROUTINE
:0045693A 81F399990000            xor ebx, 00009999             }
:00456940 31C3                    xor ebx, eax                  }
:00456942 81FB998F3337            cmp ebx, 37338F99             }
:00456948 7501                    jne 0045694B -> fuck off

:0045694A 41                      inc ecx -> ECX=2 (first serial is correct)

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0045692D(C), :00456948(C)
|
:0045694B 5D                      pop ebp
:0045694C C3                      ret


	3.1. the LODSD operand
	======================

LODSD copies the DWORD pointed by ESI into EAX and adds 4 to ESI

exemple
-------
401000 : 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08

if ESI = 401000 then after this sequence :

LOSDD
MOV EBX,EAX
LODSD

you'll have EBX = 0x04030201 and EAX=0x08070605


 	3.2. The first part of the serial check routine
	===============================================

It uses LOSDD 2 times, so we can conclude that the first serial has 8 digits. From now, I will
call serial_11 and serial_12 the two parts if the serial.

exemple : if serial = "12345678" then serial_11 = 0x34333231 and serial_12 = 0x38373635
(because 0x31 is ASCII value of '1')

OK, here is what the first part does (I hope the syntax is clear ;) :

EBX = 0x37338F99 XOR serial_12
EBX = EBX ROL 0x30
EBX = EBX XOR 0x99990000
EBX = EBX ROR 0x50
EBX = EBX + serial_11
EBX = EBX XOR 0x99999999
EBX = EBX ROL 0x70
EBX = 0x533D5375 ???

in one line :

((((((0x37338F99 XOR serial_12) ROL 0x30) XOR 0x99990000) ROR 0x50) + serial_11) XOR 0x99999999) ROL 0x70 = 0x533D5375 ?


	3.3. The second part of the serial check routine
	================================================

EBX = serial_11 ROR 0x80
EBX = EBX XOR 0x9999
EBX = EBX XOR serial_12
EBX = 0x37338F99 ???

in one line :

((serial_11 ROR 0x80) XOR 0x9999) XOR serial_12 = 0x37338F99 ???


	3.4. Let's find serial_11 and serial_12 !
	=========================================

We got 2 equations to solve :

1) ((((((0x37338F99 XOR serial_12) ROL 0x30) XOR 0x99990000) ROR 0x50) + serial_11) XOR 0x99999999) ROL 0x70 = 0x533D5375
2) ((serial_11 ROR 0x80) XOR 0x9999) XOR serial_12 = 0x37338F99


A few things to know before going on :

X XOR Y = Z <=> X = Z XOR Y
X ROL Y = Z <=> X = Z ROR Y
X ROR Y = Z <=> X = Z ROL Y

now we can rewrite the first equation :

1) ((((0x37338F99 XOR serial_12) ROL 0x30) XOR 0x99990000) ROR 0x50) + serial_11 = (0x533D5375 ROR 0x70) XOR 0x99999999

which gives :

1) ((((0x37338F99 XOR serial_12) ROL 0x30) XOR 0x99990000) ROR 0x50) + serial_11 = 0xCAECCAA4

The 2 equations are now :

1) ((((0x37338F99 XOR serial_12) ROL 0x30) XOR 0x99990000) ROR 0x50) + serial_11 = 0xCAECCAA4
2) ((serial_11 ROR 0x80) XOR 0x9999) XOR serial_12 = 0x37338F99

We can notice the same 0x37338F99 value in the two equations, let's isolate it in 1) :

1) ((((0xCAECCAA4 - serial_11) ROL 0x50) XOR 0x99990000) ROR 0x30) XOR serial_12 = 0x37338F99
2)  ((serial_11 ROR 0x80) XOR 0x9999) XOR serial_12 = 0x37338F99

Now we transpose the 'XOR serial_12' :

1) (((0xCAECCAA4 - serial_11) ROL 0x50) XOR 0x99990000) ROR 0x30 = 0x37338F99 XOR serial_12
2) (serial_11 ROR 0x80) XOR 0x9999 = 0x37338F99 XOR serial_12

hey, the two right members are the same !!! This means that the left members are equal :

3) (((0xCAECCAA4 - serial_11) ROL 0x50) XOR 0x99990000) ROR 0x30 = (serial_11 ROR 0x80) XOR 0x9999

Now can erase the 'ROR 0x80' because it does NOTHING ! (if you rotate a 32 bits value 32*X times,
the value doesn't change and of course 0x80 = 128 = 4*32)

3) (((0xCAECCAA4 - serial_11) ROL 0x50) XOR 0x99990000) ROR 0x30 = serial_11 XOR 0x9999

We transpose again :

3) 0xCAECCAA4 - serial_11 = (((serial_11 XOR 0x9999) ROL 0x30) XOR 0x99990000) ROR 0x50

As curious as it may seem, 'X XOR 0x9999 ROL 0x30 XOR 0x99990000 ROR 0x50 = 'X !!!
(you can check by yourself if you don't believe me)

3) 0xCAECCAA4 - serial_11 = serial_11

Finally :

==> serial_11 = 0xCAECCAA4 / 2 = 0x65766552



Let's find serial_12 :

2) ((serial_11 ROR 0x80) XOR 0x9999) XOR serial_12 = 0x37338F99

ROR 0x80 does nothing :

2) (serial_11 XOR 0x9999) XOR serial_12 = 0x37338F99

Finally :

2) serial_12 = (serial_11 XOR 0x9999) XOR 0x37338F99
      
Which gives :

==> serial_12 = 0x52457352


	3.5. The first serial
	=====================

serial_11 = 0x65766552
serial_12 = 0x52457352

the ASCII values of the serial are : 0x52 0x65 0x76 0x65 0x52 0x73 0x45 0x52
                                     'R'   'e'  'v'  'e'  'R'  's'  'E'  'R'

First serial = ReveRsER


+------------------------------+
| 4. Finding the second serial |
+------------------------------+

The first serial check is routine is at @4568BC

* Referenced by a CALL at Address:
|:004569BB   
|
:004568BC 89C6                    mov esi, eax
:004568BE 31DB                    xor ebx, ebx
:004568C0 31D2                    xor edx, edx
:004568C2 BB66563412              mov ebx, 12345666
:004568C7 AD                      lodsd
:004568C8 81F312505500            xor ebx, 00555012         }
:004568CE C1C350                  rol ebx, 50               }
:004568D1 F7D3                    not ebx                   }
:004568D3 31C3                    xor ebx, eax              } FIRST PART
:004568D5 F7D3                    not ebx                   }
:004568D7 81C3636C6146            add ebx, 46616C63         }
:004568DD 81FB85CC768B            cmp ebx, 8B76CC85         }
:004568E3 7514                    jne 004568F9 -> fuck off

:004568E5 AD                      lodsd                     }
:004568E6 F7D0                    not eax                   }
:004568E8 2D00000050              sub eax, 50000000         } SECOND PART
:004568ED 3D9496B168              cmp eax, 68B19694         }
:004568F2 7505                    jne 004568F9 -> fuck off

:004568F4 E877FFFFFF              call 00456870 -> registered !!!

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004568E3(C), :004568F2(C)
|
:004568F9 C3                      ret
:004568FA 8BC0                    mov eax, eax



OK, this one is easier, let's do it quickly :


	4.1. First part
	===============

EBX = NOT(12345666 XOR 0x555012 ROL 0x50) =  0xF98BED9E
EBX = EBX XOR serial_21
EBX = NOT(EBX)
EBX = EBX + 0x46616C63
EBX = 0x8B76CC85 ???

=> NOT(NOT(12345666 XOR 0x555012 ROL 0x50) XOR serial_21) + 0x0x46616C63 = 0x8B76CC85 ???

=> NOT(0xF98BED9E XOR serial_21) = 0x45156022

=> 0xF98BED9E XOR serial_21 = 0xBAEA9FDD XOR 0xF98BED9E

=> serial_21 = 0x43617243


	4.2. Second part
	================

EAX = serial_22
EAX = NOT(EAX)
EAX = EAX-0x50000000
EAX = 0x68B19694 ?

=> NOT(serial_22) - 0x50000000 = 0x68B19694
=> serial_22 = 0x474E696B


	4.3. The second serial
	======================

serial_21 = 0x43617243
serial_22 = 0x474E696B

the ASCII values of the serial are : 0x43 0x72 0x61 0x43 0x6B 0x69 0x4E 0x47
                                      'C'  'r'  'a'  'C'  'k'  'i'  'N'  'G'

Second serial = CraCkiNG


+---------+
|5. Outro |
+---------+

Thx to Falcon for an interesting crackme

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

www.tscube.cjb.net
