Hard Disk Sleeper v1.55

Tutorial de Lucifer48 [Immortal Descendants]
(23 juillet 1999)



Target Program: Hard Disk Sleeper v1.55
Location: http://www.mwso.com/
Protection: Name/Serial
Level: Beginner (3/10)



L'obtention d'un serial n'est pas difficile (le code en C est propre), mais requiert quand même un petit peu de temps. Comme d'habitude, on remplit les cases Name & Serial... Lorsque l'on appuie sur le boutton OK, il y une courte comparaison:

XXXX:00413A24  MOV  EAX,[004205D0]          ;checksum calculé à partir du nom et du serial

XXXX:00413A29  CMP  EAX,[004205C0]          ;comparaison avec la bonne valeur (19Eh)

XXXX:00413A2F  JZ   00413A6C                ;no jump = mauvais cracker!

Le calcul de la constante en [004205D0] est re-effectué à chaque pression d'une touche.

XXXX:004139DC  CALL USER32!GetWindowTextA

XXXX:004139E1  CMP  EAX,08                  ;le serial doit comporter 8 caractères

...

XXXX:00413A08  CALL 00413C94                ;fabrication de 8 octets

...

XXXX:00413A10  LEA  EAX,[EBP-0C]            ;nos 8 précieux octets (C1, C2, ..., C8)

XXXX:00413A13  PUSH EAX

XXXX:00413A14  CALL 00413D13                ;calcul du checksum (résultat eax)

XXXX:00413A19  POP  ECX                     ;add esp,4

XXXX:00413A1A  MOV  [004205D0],EAX          ;savegarde pour comparer plus tard

Ces 8 octets (C1, C2, ..., C8) sont calculés à partir du nom et du serial. Voilà (en pseudo code, mi-chemin entre le C et le Pascal) ce que fait le call 00413D13 avec ces 8 octets:

x={ C1, C2, C3, C4, C5, C6, C7, C8 }



checksum=0;

for i:=0 to 6 do

  checksum:=checksum + ( (x[i]+i) mod $A ) * ( (x[i+1]+i+1) mod $A );

Il faut qu'à la fin de la boucle checksum=0x19E Comment faire ?
Une brute force attack ?? Pas question 8 variables de 0 à FF c'est beaucoup trop. On va plutôt ce servir de la présence des modulos, en effet on ne peut pas dépasser 9*9 (et au pire 0*0) pour une boucle. Donc on va dans un premier temps chercher à trouver ces 8 nombres entre 0 et 9. J'explique autrement, si vous préférez, on effectue un changement de variable; D1= (C1 + 0) mod 0xA, etc... donc la formule avec le checksum peut s'écrire (formellement parlant):

y={ D1, D2, D3, D4, D5, D6, D7, D8 }



checksum=0;

for i:=0 to 6 do

  checksum:=checksum + y[i] * y[i+1];

et plus simplement, cela est équivalent à:

checksum = D1*D2 + D2*D3 + D3*D4 + D4*D5 + D5*D6 + D6*D7 + D7*D8

A ce moment là, la "brute force" est tranquille (8 variables, allant de 0 à 9, seulement (10^8 =) 100 millions de possibilités, de la rigolade...).

Voilà ma source en C:

/*********************************************************/

#include 



void main()

{

signed char x1, x2, x3, x4, x5, x6, x7, x8;

long a;



for(x1=0; x1<=9; x1++)

 for(x2=0; x2<=9; x2++)

  for(x3=0; x3<=9; x3++)

   for(x4=0; x4<=9; x4++)

    for(x5=0; x5<=9; x5++)

     for(x6=0; x6<=9; x6++)

      for(x7=0; x7<=9; x7++)

       for(x8=0; x8<=9; x8++)

       {

       a=0;

       a= x1*x2 + x2*x3 + x3*x4 + x4*x5 + x5*x6 + x6*x7 + x7*x8;

       if (a==0x19E)

         printf("%x %x %x %x %x %x %x %x\n",x1,x2,x3,x4,x5,x6,x7,x8);

       } /* end of for */

}

/*********************************************************/

On trouve des tonnes de solutions (2919 exactement) en quelques secondes. Voici les dernières:

a b c d e f g h



9 9 9 9 9 4 6 5

9 9 9 9 9 4 9 2

9 9 9 9 9 5 5 4

9 9 9 9 9 5 9 0

9 9 9 9 9 6 3 6

9 9 9 9 9 6 4 3

9 9 9 9 9 6 6 0

9 9 9 9 9 7 3 2

9 9 9 9 9 8 2 1

9 9 9 9 9 9 1 0

Si je prend le dernier exemple; ca veut dire que (en regardant la source du programme):

Boucle1:	(C1 + 0 + 0) mod 10 = 9  et  (C2 + 0 + 1) mod 10 = 9

Boucle2:	(C2 + 1 + 0) mod 10 = 9  et  (C3 + 1 + 1) mod 10 = 9

Boucle3:	(C3 + 2 + 0) mod 10 = 9  et  (C4 + 2 + 1) mod 10 = 9

Boucle4:        (C4 + 3 + 0) mod 10 = 9  et  (C5 + 3 + 1) mod 10 = 9

Boucle5:        (C5 + 4 + 0) mod 10 = 9  et  (C6 + 4 + 1) mod 10 = 9

Boucle6:        (C6 + 5 + 0) mod 10 = 9  et  (C7 + 5 + 1) mod 10 = 1

Boucle7:        (C7 + 6 + 0) mod 10 = 1  et  (C8 + 6 + 1) mod 10 = 0

On enlève les doubles et on obtient les conditions suivantes:

(C1 + 0) mod 10 = 9 (= a)

(C2 + 1) mod 10 = 9 (= b)

(C3 + 2) mod 10 = 9 (= c)

(C4 + 3) mod 10 = 9 (= d)

(C5 + 4) mod 10 = 9 (= e)

(C6 + 5) mod 10 = 9 (= f)

(C7 + 6) mod 10 = 1 (= g)

(C8 + 7) mod 10 = 0 (= h)

Il est donc facile de générer nos 8 précieux octets (C1, C2, ..., C8). S'en est fini pour le call 00413D13.

Remarque: N'oublions pas que ces huits octets proviennent directement du nom et du serial.

On sait comment obtenir 8 bons octets (C1...C8), maintenant, étudions la façon dont on arrive à ces 8 octets (en encodant le nom et le serial). Ca se trouve dans le CALL 00413C94 (en XXXX:00413A08). Posons S1, S2, ..., S8 les huits caractères du serial définitif.

Mon nom (Lucifer48) est codé comme ceci (84 = 4C + 38) sur 8 octets:

XXXX:0068F464  84 75 63 69 66 65 72 34                          .ucifer4

               N1 N2 N3 N4 N5 N6 N7 N8

Le procédé d'encrytion (c'est un bien grand mot) est le suivant:

C1 =S1 - [ (N1 mod 1Ah) + 41h ]

C2 =S2 - [ (N2 mod 1Ah) + 41h ]

...

C8 =S8 - [ (N8 mod 1Ah) + 41h ]

Si Ck (k={1,2,...8} ) est négatif (compris entre 7F et FF), alors Ck += 1Ah.
Chaque caractère du serial est indépendant des autres. Donc on va pouvoir résoudre succéssivement 8 équations. En "retournant" le problème, on obtient:

S1 = C1 + [ (N1 mod 1Ah) + 41h ]

S2 = C2 + [ (N2 mod 1Ah) + 41h ]

...

S8 = C8 + [ (N3 mod 1Ah) + 41h ]

A ceci s'ajoute la condition: si on suppose que Sk > [ (Nk mod 1Ah) + 41h ]
(afin d'écarter le problème de la retenue (+1A) ).

Pour mon nom:

S1 = C1 + 43

S2 = C2 + 4E

S3 = C3 + 56

S4 = C4 + 42

S5 = C5 + 59

S6 = C6 + 58

S7 = C7 + 4B

S8 = C8 + 41

Pour trouver C1, C2, ..., C8 je fais:

C1 = 3*Ah + 9 - 0 = 27

C2 = 3*Ah + 9 - 1 = 26

...

C7 = 3*Ah + 1 - 6 = 19

C8 = 3*Ah + 0 - 7 = 17

Je prend ce 3 car c'est une constante idéale qui nous permet d'avoir des caractères du serial printables. J'otiens ça:

6A ; 74 ; 7B ; 66 ; 7C ; 7A ; 64 ; 58 (soit: "jt{f|zdX")

Le serial marche mais il y a deux caractères pas super ;) En fait le 3 n'est pas si formidable que ça ! on va prendre 2 et là ca sera mieux ! (même démarche que ci-dessus):

C1 = 2*Ah + 9 - 0 = 1D

C2 = 2*Ah + 9 - 1 = 1C

...

C7 = 2*Ah + 1 - 6 = 0F

C8 = 2*Ah + 0 - 7 = 0D

J'obtiens:
60 ; 6A ; 71 ; 5C ; 72 ; 70 ; 5A ; 4E (soit: "`jq\rpZN")

Youpi, ca marche encore !! Cette constante 2 n'est vraiment pas mieux !
Finalement, je me résoud à mixer les deux serials pour obtenir ma registration:

Name/ Lucifer48

Serial/ jjqfrzdX


Greetings: All ID members (Volatility, Torn@do, ...), Eternal Bliss, ACiD BuRN, people on #cracking4newbies, french crackers, etc.



(c) Lucifer48. All rights reversed