Name: Lucifer48   Code: 3550318047
Name: Lucifer48   Code: 3629595978

Programme  : CrAcK me #2 By THE_q/PC
PlateForme : Windows 95
Date       : 14h, 3 juillet 1998 (France-Italie à 16h30)
Protection : Registration
Fichier    : Crackme2.exe
Outils     : Soft-ice v2.0
Ou ça?     : http://cracking.home.ml.org (un très bon site)
Temps passé: Quelques heures
Cours      : 09
Matos      : Bloc-notes en 800*600

Aujourd'hui, on s'attaque à un Crackme.
Le but c'est de trouver, le bon code correspondant à mon nom.
Je lance le programme, je mets les infos suivantes:
Name: Lucifer48
Code: 36153615

=====================
1. APPROCHE THEORIQUE
=====================

Sous soft-ice je mets un breakpoint hmemcpy. Je click sur OK
F5, F11 et je trace. J'arrive sans difficuté ici:

XXXX:0040130A  3C00        CMP  AL,00
XXXX:0040130C  743F        JZ   0040134D
XXXX:0040130E  90          NOP
XXXX:0040130F  90          NOP
XXXX:00401310  90          NOP
XXXX:00401311  90          NOP
XXXX:00401312  E85E000000  CALL 00401375
XXXX:00401317  E8A5000000  CALL 004013C1
XXXX:0040131C  85C0        TEST EAX,EAX
XXXX:0040131E  742D        JZ   0040134D   <------ Test important
XXXX:00401320  90          NOP

Le call 00401375 sert à encoder le nom entré, il prend les 8 premiers caractères, additionne
le premier avec le dernier... effectue un xor avec le nom PhroZenQ... Allez y faire un tour
si ça vous intéresse. C'est de l'assembleur à comprendre (ni + ni -).

Que fait en gros le crackme:
- Encode (sorte de checksum) notre Nom (sur 10 octets) et notre Code (sur 4 octets =32 bits)
- Le CALL 004013C1 effectue une serie de manipulation sur notre nom et ressort true ou false
  (EAX=1 ou EAX=0)

Explorons plutôt ce CALL 004013C1, voiçi la routine entière:

XXXX:004013C1 MOV  EAX,[004021D2]  ;Pointe sur notre code (encodé) et le met dans EAX
XXXX:004013C6 MOV  CL,[004021A9]   ;Pointe sur le 1er octet du nom encodé, le met dans CL
XXXX:004013CC ROL  EAX,CL          ;Rotation du registre EAX de CL bits vers la gauche
XXXX:004013CE MOV  ECX,[004021AE]  ;Charge EAX avec les 4 derniers octets de mon nom encodé
XXXX:004013D4 XOR  EAX,ECX         ;ou exclusif...
XXXX:004013D6 MOV  EBX,[004021AA]  ;Chage EBX avec les 2,3,4,5 èmes octet de mon nom encodé
XXXX:004013DC MOV  CL,AL           ;pas besoin d'explications
XXXX:004013DE ROR  EBX,CL          ;Rotation du registre EBX de CL bits vers la droite
XXXX:004013E0 XOR  EAX,EBX         ;<-------- ou exclusif très important
XXXX:004013E2 MOV  EAX,00000000    ;c'est à dire XOR EAX,EAX
XXXX:004013E7 JNZ  004013EF	   ;si on saute: mauvais code (dans ce cas EAX=0)
XXXX:004013E9 NOP
XXXX:004013EA NOP
XXXX:004013EB NOP
XXXX:004013EC NOP
XXXX:004013ED INC  AL		   ;Si EAX=1, alors bon code
XXXX:004013EF RET		   ;fin du call

Vous l'avez compris, notre but c'est que, à la fin des manipulations (ROL,XOR,ROR) on aboutisse
à EAX=EBX pour que l'instruction XOR EAX,EBX mette à 0 EAX et par le même occasion le flag
ZF=1 et donc pas de saut et donc c'est un bon code.


=================
2. PHASE PRATIQUE
=================

Calcul du bon serial:
Avec les données que j'ai entrée, voilà mes données encodés en mémoire:
Code: 0F A9 27 02
 Nom: D0 8F BA A0 DA 82 A6 9E 38

 MOV EAX,[004021D2] donne comme résultat EAX= 0227A90F
 MOV CL,[004021A9]  donne comme résultat CL= D0
 MOV ECX,[004021AE] donne comme résultat ECX= 389EA682
 MOV EBX,[004021AA] donne comme résultat EBX= DAA0BA8F

Rappel: x_d est une valeur décimale et x_h est une valeur héxadécimale (voir cours 08)

Soit X notre bon serial encodé (sur 32bits), que l'on recherche
Soit Y les 8 premiers bits (= 1 octet) de X à l'issue de l'instruction XOR EAX,ECX
Donc pour avoir le bon code il faut que:

     ( ROL X,D0_h ) XOR (389EA682) = ROR DAA0BA8F,Y

Trouver une relation entre X et Y va être difficile car Y dépend directement du résultat
de ( ROL X,D0_h ) XOR (389EA682)

Culture générale:  A XOR B <=> (A OR B) AND ( NOT (A AND B) )
		           <=> (A OR B) AND ( (NOT A) OR (NOT B) ) <=> A (+) B

Il faut donc prendre une valeur pour Y.

Résumons nous autrement:
-Soit Y le premier octet (le + à droite) de ( ROL X,D0_h ) XOR (389EA682)
-Soit Z le premier octet (le + à droite) de ( ROL X,D0_h )
Il faut que Z XOR 82 = Le premier octet de ROR DAA0BA8F,Y
		     = Y (à partir de XXXX:004013D6)
Finalement Y = Le premier octet (le + à droite) de ROR DAA0BA8F,Y
	   ------------------------------------------------------------
La voilà notre condition.

* Supposons que Y=01 alors Z XOR 82 = 47 (premier octet de ROR DAA0BA8F,01)
 	              mais Z XOR 82 = 01 (condition)
  Ca ne va donc pas (47<>01)

* On va pas essayer toutes les valeurs au hasard. C'est le moment de passer la main
  à l'amie fidèle du cracker: la calculatrice.
  J'improvise un petit programme (que voilà):
  << 32 STWS #0h #DAA0BA8F DO RR SWAP 1 + SWAP DUP #FFh AND UNTIL 3 PICK == END >>
  Concis mais efficace... je l'exécute et il me ressort: #1F et #B541751F
  (Remarque: #D5 et #5D47ED5 est aussi une solution possible)
  En effet: ROR DAA0BA8F,1F = B541751F
			 --         --
  On voit que notre condition est respectée. Finalement
  
    ROL X,D0_h XOR (389EA682) = B541751F 
<=> ROL X,10_h XOR (389EA682) = B541751F
<=> (389EA682) XOR (B541751F) = ROL X,10_h
    Remarque: D0_h = 32 * 6_h + 10_h, soit une rotation de 16 bits (= XCHG AL,AH par ex)
    On trouve que ROL X,10_h = 8DDFD39D
              <=> ROR 8DDFD39D,10_h = X
	      <=> X = D39D8DDF (enfin!, ceci est donc l'encodage en mémoire de mon nom)


===================================
3. COMMENT EST ENCODE NOTRE SERIAL?
===================================

Notre serial encodé est mis dans EAX après l'exécution en
XXXX:00401300  E821010000  CALL 00401426
Ce call est en fait CALL [User32!GetDlgItemInt] (merci wdasm, bon d'accord ma version de
soft-ice est un peu trop préhistorique...,je n'avais pas modifié winice.dat) 
qui renvoit dans EAX la valeur d'un serial convertie en héxadécimale.
Ici: EAX vaut 0227A90F, or 227A90F_h = 36153615_d (ça me dis qqch...)
Deplus D39D8DDF_h = 3550318047_d
On recommence donc la manip:
Name: Lucifer48
Code: 3550318047

Ca marche! C00L!