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