Envoyer un mail

OllyView Keygenme 3

Tutorial redigé le 08.09.2004 par SeVeN / FFF

Outils

  • OllyDbg
  • Un éditeur hexadécimal (ici: Winhex)
  • c'est tout...

Introduction

Avant de faire autre chose, j'ouvre toujours un keygenme sous PEiD, pour verifier s'il est packé ou non. Ici, ce n'est pas le cas: PEiD nous indique "Borland Delphi 6.0 - 7.0". On execute donc le keygenme sous OllyDbg, et là, c'est le drame, le debuggeur se ferme lâchement.

Analyse

Enlever les Anti-Debug...
Intuitivement, on peut se dire que le keygenme recherche dans les titres des fenêtres le texte 'OllyDbg' (c'est une détection de OllyDbg classique). On cherche à tout hasard dans le binaire à l'aide d'un éditeur hexadécimal, et en effet on tombe sur "OllyDbg", "DeDe" et "URSoft W32Dasm", que l'on patch bêtement en "XllyDbg", "XeDe" et "XRSoft W32Dasm". Ca fonctionne, maintenant le keygenme se lance sous OllyDbg sans aucun soucis.

Cependant, en traçant la routine de vérification du serial, on remarque un 2ème Anti-Debug. Je vais l'expliquer maintenant même si on ne le rencontrera que plus tard :

OllyDbg ScreenCapture 0

Quand le debuggeur est détecté, alors BYTE PTR DS:[457C0D] vaut 01, sinon il vaut 00. Ce byte est ensuite utilisé dans le calcul du serial.

En posant un hardware breakpoint en 457C0D, et en relançant le keygenme, OllyDbg break ici :

OllyDbg ScreenCapture 1

Ici il récupère donc les noms des classes des fenêtres, à l'aide de l'API GetClassNameA, et les compare avec des noms prédefinis (OLLYDBG, HexworksClass, TDeDeMainForm, ...). S'il détecte une de ces classes, il ne se ferme pas, mais met à 1 la variable utilisée plus tard. Pour contrer cet anti-debug, il suffit de poser un ret au debut de la fonction, en 454744.
Forme du serial
On continue donc, et on entre un nom et un serial au hasard. Pour moi ça sera mon tradionnel SeVeN/123456. On valide, une MessageBox apparait ('Have a nice day !') et le keygenme se ferme. On ouvre donc de nouveau tout ça, et on recherche "Have a nice day" dans les referenced text strings. La réference étant en 45435F, on pose un breakpoint en -presque- début de routine, c'est à dire en 4542A1, juste apres que le keygenme ai verifié si on a bien entré quelque chose.

Voila à quoi ressemble le code ici:

OllyDbg ScreenCapture 2

On remarque tout de suite une boucle qui effectue des operations sur le nom et sur une constante, "e_"{R1ds\[N*@y=&", que j'ai nommée key. L'opération effectuée est la suivante:

pour i de 1 à Taille(nom)
   resultat[i] = caractère(((((valeur_ascii(nom[i]) - $20) + i) + valeur_ascii(key[i])) modulo $5F) + $20)


A la fin de cette boucle, on a donc une chaine de caractères, de la même taille que le nom.
Pour SeVeN, on obtient Zg{&F. Continuons à tracer...

En 454346, un call nous renvoit la taille du serial, et en 454350, celle du nom, qu'il place en EAX. EAX subit deux opérations (shl eax, 2 et add eax, 7), et est comparé à la taille du serial. Je relance donc OllyDbg en prenant soins de mettre un serial de 27 caractères pour mon pseudo. (5 shl 2 = 20, 20 + 7 = 27)

Ensuite, il vérifie si le serial commence par 'CrK-' et se termine bien par '-666'. Si c'est le cas, on arrive ici :


OllyDbg ScreenCapture 3

Cette boucle vérifie si tous les 4 caractères on trouve un tiret ($2D). Ceci nous permet donc de dire que le serial pour SeVeN sera de la forme CrK-xxx-xxx-xxx-xxx-xxx-666.
Vérification du serial
Je vais donc recommencer avec SeVeN/CrK-012-345-678-901-234-666.

OllyDbg ScreenCapture 4

La boucle débute en 454430, et se termine en 4544FB. Dans cette boucle, le keygenme va récuperer les triplets ainsi délimités (012, 345, 678, ...) et effectuer des opérations dessus, opérations que l'on peut résumer par:

pour i de 1 à nombre_de_triplets
   resultat[i] = caractère(((((a + b) * c) - $20) mod $5F) + $20)


où :
a = valeur ascii du 1er caractère
b = valeur ascii du 2nd caractère
c = 3ème caractère (c étant donc une valeur numérique)

C'est ici qu'apparait le second Anti-Debug, en 4544BB. A la fin des opérations, on obtient une chaine de caractères, ici c(piw.

Finalement, cette chaine est comparée avec la chaine précedemment génerée à partir du nom. Si les deux chaines sont identiques, alors le serial est valide.

Conclusion

On a en premier lieu un calcul à effectuer sur le nom, afin d'obtenir une chaine de caractères.
On doit faire en sorte que le calcul sur le serial nous donne le même resultat. Pour cela, je propose un mini-bruteforce.

Pour chaque caractère du résultat du calcul sur le nom, on va chercher le triplet qui résoud l'équation. Pour cela, on essaie toutes les valeurs possibles:
  • le 1er caractère peut être n'importe quel caractère,
  • le 2nd caractère aussi,
  • le 3ème ne peut être qu'un chiffre.

Algorithme (Delphi)

function IntToStr(Num: Integer): string;
begin
  Str(Num, result);
end;

function First(nom: string): string;
var
  key: string;
  i: integer;
begin
  key := 'e_"{R1ds\[N*@y=&';
  while (length(key) < length(nom)) do key := key + key;
  for i := 1 to length(nom) do
    Result := Result + chr(((((ord(nom[i]) - $20) + i) + ord(key[i])) mod $5F) + $20);
end;

function Second(crc: byte): string;
var
  a, b, c, x: integer;
begin
  for a := $30 to $39 do
    for b := $30 to $5A do
      for c := 0 to 9 do
        if (((((a + b) * c) - $20) mod $5F) + $20 = crc) then
        begin
          Result := chr(a) + chr(b) + IntToStr(c);
          x := Random(777);
          if ((x mod 7 = 0) and not ((b>$39) and (b<$41))) then Exit;
        end;
end;

procedure Generate;
var
  Buffer: array[0..255] of char;
  i: integer;
  crc, nom, serial: string;
begin
  i := SendMessage(Edit_Nom, WM_GETTEXTLENGTH, 255, LongInt(@Buffer));
  if i > 0 then
  begin
    SetLength(nom, i);
    SendMessage(Edit_Nom, WM_GETTEXT, i + 1, lParam(PChar(nom)));
    crc := First(nom);

    Randomize;
    for i := 1 to length(crc) do
      serial := serial + Second(ord(crc[i])) + '-';

    serial := 'CrK-' + serial + '666';

  end else serial := 'Veuillez entrer un nom';
  SendMessage(Edit_Serial, WM_SETTEXT, 0, Integer(serial));
end;

La fonction IntToStr permet d'éviter d'utiliser l'unité SysUtils, afin d'alléger le keygen. N'oublions pas qu'on code ici sans la VCL.

La fonction First effectue le 1er calcul, sur le nom entré, en fonction de la clé hardcodée.

La fonction Second effectue le bruteforce, et permet d'obtenir un triplet de caractères correspondant à un caractère du resultat du calcul prédédent sur le nom. Le triplet sera de la forme [0-9]|[0-9][A-Z]|[0-9] afin de générer un serial "propre". J'utilise un Random afin de ne pas avoir le 1er résultat du bruteforce à chaque fois.

Enfin, la procédure Generate récupère le nom, appelle les 2 fonctions précedentes, et affiche le serial.


Téléchargement

- Le Keygenme.
- Le Keygen.
- Ce tutorial zippé.


Remerciements

- FFF, ma famille de cracking.
- Toutes les personnes avec qui j'ai le plaisir de discuter sur IRC.
- Les personnes et teams citées sur mon site.
- Et celles que j'ai oubliées...
- (sans oublier celle que j'aime encore, et que tout le monde reconnaitra...)

SeVeN / FFF

xhtml validator css validator Get Firefox Powered by PHP

Google