dare2crackme By DooMeeR
par Kharneth
 
Outils utilisésPublicCible
 - PeID
 - OllyDbg
 - Compilateur
 - Google
 - Papier, Crayon, Cerveau 5.0
 Débutant / avancé en Cracking
ayant de bonnes connaissances en programmation
 dare2crackme.exe
 
I - PeID 

      Un rapide scan avec PeID nous indique Borland Delphi 6.0 - 7.0, mais nous avions reconnu de toute façon l'icône caractéristique des programmes Delphi. Puisque le programme n'est pas compressé, nous allons l'attaquer de suite avec OllyDBG!

II - OllyDBG 

      Le problème avec Delphi et sa VCL (Visual Component Library / Bibliothèque de composants visuels), c'est que les apis classiques (MessageBox, GetWindowText...) ne fonctionnent pas! En effet, les MsgBox sont créées de toute pièce et les chaînes des champs texte sont récupérées par CallWindowProc(...WM_GETTEXT...). Le tout noyé dans d'innombrables Call! Mais heureusement pour nous, le compilateur Delphi est très bavard et laisse trainer plein de chaines intéressantes!! :)
      En cherchant dans les String Data Ref, on ne trouve pas le texte de la MsgBox, "Invalid key!". Celui-ci est probablement crypté. On va donc chercher les noms des fonctions gérants les évènements (du type Button1Click).

      Après sélection de la ligne, on appuie sur la touche Entrée pour se retrouver dans le listing.

      On sélectionne l'adresse juste au dessus de la chaine puis on appuie sur Entrée pour se retrouver au début de la procédure de gestion de l'évènement Button1.onClick().

      On remarque la chaîne affichée dans la MsgBox lorsque l'on click sur le bouton "Crackme's goal". La procédure appelée ensuite correspond à la fonction ShowMessage() qui va construire une MsgBox puis afficher la chaîne passée en paramêtre. On oublie pas de définir un Label (Click-droit --> Label) lorsque l'on a identifié une fonction pour voir plus facilement les différents appels à cette fonction.
      Il existe 2 méthodes pour passer des paramêtres à une fonction, soit par la pile, soit par les registres. Delphi utilises les registres lorsque les options d'optimisation sont sélectionnées. Pour plus d'informations: http://nono40.developpez.com/tutoriel/delphi/asm/.
      On réitère l'opération précédente avec les 2 autres chaînes intéressantes à savoir "OKBtnClick" et "FoncClick". (On verra plus tard qu'il y a un troisième bouton caché. Mais la fenêtre permet l'agrandissement, donc en la mettant en plein écran, on peut voir un texte ainsi que ce troisième bouton.). Il suffit de placer un BreakPoint au début de ces 2 procédures puis de lancer le programme.
      On tape un serial bidon puis on click sur le bouton "OK". OllyDBG s'arrête alors au début de la procédure "OKBtnClick".

      J'ai indiqué le nom des fonctions pour bien comprendre à quoi elles correspondent, malheureusement, Olly ne sait pas le faire! :( Oui je sais, il y a Dede mais c'est pour montrer que finalement Olly suffit! :p
      En étudiant la procédure, on voit qu'un Checksum est calculé sur le pass. Si cette valeur égale 432, le programme agrandit la fenêtre pour laisser apparaitre un texte ainsi qu'un nouveau bouton.
      Voici la fonction de calcul du Checksum:

      Elle multiplie chaque caractère entre eux (dans une variable 32 bits), divise le résultat par 1000 puis renvoit le reste. Et ce reste doit être égal à 432. Un petit brute force rapidement codé permet de trouver d'innombrables pass valides en se limitant à 4 caratères. Par exemple XMEN fonctionne! :)
      Maintenant, on fait une pause cigarette, coca, tequila, pipe... Et une fois bien décontracté, on click sur le bouton "Some awsome fonctionnality". Et là Olly s'arrête puisqu'on avait placé un BP au début de la procédure "FoncClick".

      On voit l'importance des Labels car on peut ainsi identifier rapidement les différentes fonctions utilisées ailleurs dans le programme. On voit donc qu'on nous refait un coup de (Checksum(pass) Modulo 1000) == 432. Par contre, ensuite, ça se complique. On voit 2 appels à l'api VirtualProtect pour modifier les droits d'accès à une zone du code. En général pour écrire dedans en cas de code crypté par exemple. L'adresse en question est 00457078 (visible en haut de la capture). Le premier autorise l'écriture et le deuxième rétablit les droits d'origine.
      On voit entre les 2 VirtualProtect que les 4 premiers caractères du pass subissent un XOR CAB8DB92 et que le DWORD résultant écrase les 4 octets à cette fameuse adresse. Et juste après le 2ème VirtualProtect, le programme appelle cette adresse dont le code vient d'être modifié. Evidemment, ça plante et l'aventure s'arrête là!
      Ou plutôt, c'est là qu'entre en scène la fameuse intuition du Cracker (sisi vous savez les trucs que vous faites des fois sans savoir pourquoi...)! :)
      Un petit retour en arrière s'impose! Au début du programme il manque la chaine "Invalid key!". Hors elle est bien visible après exécution. Et on peut supposer qu'il existe également un message de félicitation. De plus, si l'on a été attentif / curieux, on a remarqué juste au-dessus de la fonction "ModChecksum" en 00456D64, la chaîne "You cracked me!". Il faut donc trouver l'endroit où elle est créée.

      Rien de bien compliqué, Click-droit sur l'adresse 00456D64 puis "Follow in dump --> Constant", puis après sélection du premier caractère (Y), Click-droit puis "Breakpoint --> Hardware, on access --> Byte". Il suffit maintenant de relancer le programme avec Ctrl+F2 puis d'appuyer sur F9 tant que l'octet en 008B25A4 n'est pas 'Y'.
      On arrive au milieu d'une procédure qui commence en 00457118 et qui place des octets à une adresse mémoire. Si l'on s'amuse à convertir ces octets en caractères on retrouve les 2 chaines "Invalid key!" et "You cracked me!". Mais ce n'est pas tout! A la fin de cette procédure, on retrouve 2 nouveaux appels à VirtualProtect.

      On remarque que l'adresse dont les octets vont être modifiés est 00456D64. C'est à dire, celle que l'on a vu précédemment qui contenait l'adresse de "You cracked me!". Et effectivement, au chargement du programme, cette adresse contient 4 nop (90909090).
      Bon et maintenant on fait quoi avec tout ça??? Récapitulons:
On sait que le modulo checksum doit être égal à 432 mais surtout que les 4 premiers caractères du pass xor constante doivent donner un code exécutable! Donc on va d'abord chercher quel code cela pourrait être, ensuite un brute force nous permettra de trouver un pass qui remplira la première condition.

      En 00457078 nous avons 6 octets 89 D4 F8 8B FF C3. Les 4 premiers vont être remplacés, ce qui nous laisse FF et C3 (Retn). Nous avons aussi cette procédure en 00456D6C qui utilise la chaîne "You cracked me!" mais qui ne semble appelée de nulle part! De plus le Call n'est apparemment pas ShowMessage, sinon le Label apparaitrait. Après quelques réflexions et avec un peu d'expérience, on peut supposer que ce code inconnu correspondrait à un Call intermédiaire qui appellerait cette procédure.
      D'abord, la procédure qui utilise "You cracked me!" se termine par un ret, donc il lui faut une adresse de retour et un Call convient puisqu'il fournit son adresse de retour. Ensuite, le dernier octet est FF ce qui est le cas quand un Call appelle une procédure pas trop éloignée de lui.
      Donc en patchant sous olly, on se met à l'adresse 00457078, puis on tape Call 00456D6C. On obtient les 4 octets suivants: E8 EF FC FF avec le FF C3 final, ça colle! :)
      Pour retrouver les 4 premiers caractères du pass, on va appliquer le xor sur ces 4 octets (CAB8DB92 est mis à l'envers à cause de la notation Little endian 32bits). Ce qui donne:

92DBB8CA Xor E8EFFCFF = 7A344435 soit z4D5

      Le modulo Checksum de z4D5 n'étant pas correct, on va utiliser un brute force pour ajouter 1 ou plusieurs caractères à la fin jusqu'à avoir un modulo juste.
      Exemple de mot de passe valide: z4D53M.

III - Brute Force 
      Voici un exemple de Brute Force codé sous Dev-C++, qui renvoit les résultats dans un fichier texte.
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {

    int a,b,c,d, i = 0;
    unsigned int sum = 1;
    unsigned int mod = 0;
    char pass[8] = "z4D5";
    pass[6] = 13;pass[7] = 10;  // Retour à la ligne
    pass[8] = 0;
    FILE *pFile = 0;
    pFile = fopen("res.txt", "wt");

    for (a=0x30;a<0x7B;a++) {    pass[4] = a;
    for (b=0x30;b<0x7B;b++) {    pass[5] = b;
        sum = 1;
        for (i=0;i<6;i++) {
            sum *= pass[i];
        }
        mod = sum % 0x3E8;
        if (mod == 0x1B0) {
           fputs(pass, pFile);
        }
    }
    }
    fclose(pFile);
    system("pause");
    return 0;
}
Kharneth 

He went into the room where his sister lived,
and...then he


Merci à toutes les personnes qui se battent pour que l'Information soit accessible à tous!