32 0 106KB
Tutoriel Débogage et cracking pratiques 17-07-2012, 14:53 Bonjour, Nous allons maintenant avoir une introduction pratique aux fonctions d'un débogueur. Cela signifie que je ne parlerai pas en profondeur de tout ce qu'il est possible de faire (impossible), et que je ne donnerai pas non plus de notions de langage assembleur. Mais bien sûr, personne ne sera déconcerté. Introduisons le concept de "debug". Au départ, il est bon de dire que débogueur n'est pas la même chose que désassembleur. Un désassembleur transforme le langage machine, transcrit à partir des instructions envoyées au processeur, en langage assembleur, contrairement au langage machine, lisible par l'homme. Les débogueurs analysent et testent les applications. Si vous travaillez avec la programmation, vous devez avoir déjà travaillé avec un, car tout bon IDE en a toujours un avec lui. Ils sont largement utilisés pour la gestion des erreurs. Les débogueurs exécutables déjà compilés (binaires) fonctionnent de la même manière, mais ils utilisent un désassembleur avant d'exécuter leur action. Aucun de ceux-ci ne peut être confondu avec un décompilateur. Concepts de base Bon, maintenant je suis professeur d'informatique du coin. Savez-vous ce qu'est la mémoire RAM et quelle fonction ? Bien sûr que vous le savez, mais remplissons la saucisse. La RAM (mémoire à accès aléatoire) est volatile et sert de stockage temporaire pour les fichiers, les programmes et les données des programmes en cours d'exécution. Le processeur est le cerveau de l'ordinateur, exécutant des instructions et des opérations. L'adressage mémoire est une sorte de table qui indique au processeur où se trouvent certaines données en mémoire. La pile est une structure de stockage de données. Vous avez peut-être déjà entendu parler de « débordement de pile », n'est-ce pas ? Les registres sont des morceaux de mémoire situés sur le processeur, temporairement utilisés pour effectuer des opérations. Les processeurs 32 bits peuvent stocker 32 bits dans chaque registre. Processeurs 64 bits... Les registres ont une vitesse d'accès élevée. Sa capacité (et sa quantité) dépend totalement du processeur. Par exemple, un processeur Intel i386 32 bits possède 9 registres 32 bits. Ce sont : EAX, EBX, ECX, EDX, ESP, EBP, ESI, EDI et EIP. En théorie, chacun a sa fonction, mais en pratique ils s'utilisent tous comme les autres, puisque le programmeur peut les utiliser à sa guise, avec une seule restriction : ne pas toucher au dernier registre (EIP), car il a l'instruction fonction d'adressage. Si vous le modifiez, le flux d'exécution de votre programme changera, ce qui pourrait entraîner une opération illégale ou une erreur de segmentation. Assemblage de base Bien sûr, nous n'allons pas voir grand-chose en assemblage, juste une introduction. Cependant, je vous conseille d'approfondir vos études dans cette langue, si ce domaine vous intéresse vraiment. C'est un langage de bas niveau (haute performance, syntaxe différente du langage humain, travaille directement avec l'architecture et les registres du processeur), qui lit les opcodes (codes opérationnels, instructions envoyées et interprétées par le processeur) et les convertit
en ses mnémoniques ( mots de langue réservés qui constituent la syntaxe). Asm a sa difficulté de syntaxe, ce que vous n'avez pas l'habitude de voir. Pensez à "goto" de Basic, AutoIt1-2 et Batch. C'est dans ce style. Les débogueurs actuels peuvent commenter le code et le délimiter, ce qui facilite le travail. Mais il n'y a rien pour épargner le look, le papier et le stylo. Peu importe combien vous donnez un nom à une variable lors de la programmation d'un logiciel, au moment de la compilation, il est converti en l'adresse mémoire que la variable occupe. Chaque instruction a son adresse, et c'est sur cette base que fonctionnent les JUMPS (sauts). Examinons maintenant certaines des instructions de base (quelque chose comme les fonctions natives). MOV destination,source Déplace la valeur d'un champ à un autre, pouvant travailler avec des constantes, des valeurs, des piles, etc. CMP/TEST string1,string2 Comme la fonction C strcmp(), effectue une comparaison entre les deux arguments. Dans le cas de TEST, vous pouvez également vérifier si une valeur est plus petite que l'autre (et, bien sûr, si elle est plus grande que l'autre). L'instruction CMP n'effectue qu'une soustraction entre les deux valeurs. Si le résultat est nul, les deux valeurs sont égales (12345-12345=0 :. 12345=12345). Adresse JMP Passe à une adresse spécifique (nous verrons les adresses plus tard). L'adresse JE/JZ (Saut si égal) saute à une adresse si la dernière comparaison a renvoyé vrai. L'adresse JNE/JNZ (Saut si différent) saute à une adresse si la dernière comparaison a renvoyé un résultat négatif. ADD/SUB/MUL/DIV destination, value Effectue l'une des 4 opérations de base. PUSH value Ajoute la valeur en haut de la pile, souvent utilisée dans les appels (CALL), car une fonction récupère ses arguments dans les piles. Valeur POP Supprime la valeur du haut de la pile. Notez que, comme pour une pile de livres, vous ne pouvez accéder aux données de la pile que du dernier au premier. CALL func Appelle une fonction interne ou externe (depuis une DLL). Bien sûr, il existe de nombreuses autres instructions, mais ce sont celles que nous examinerons le plus. Nous avons déjà parlé des registres, mais au-delà d'eux, nous avons aussi des drapeaux, des bits couramment utilisés comme résultats d'opérations (un booléen - 1 (vrai) ou 0 (faux) - par exemple). C'est pourquoi ils sont utilisés dans les comparaisons (CMP/TEST). Les plus connues sont : ZF/Z Une opération aboutie à zéro (dans le cas d'un CMP, les deux valeurs sont égales). CF/C
Le résultat d'une opération a dépassé la valeur maximale supportée par le registre (un débordement). SF/S Indique une valeur négative. Toutes les applications dans ASM sont basées sur une adresse. Les variables que vous créez pendant le développement deviennent des adresses (une zone de mémoire). Hops (JMP) fonctionne également avec ces adresses. OllyDbg Apprenons maintenant à connaître l'interface principale de Seuls les utilisateurs enregistrés et activés peuvent voir les liens., Cliquez ici pour vous inscrire... . Bien sûr, la curiosité vous aidera plus que n'importe quel tutoriel. Ici, je vais juste vous montrer par où commencer. La fenêtre est divisée en quelques sections. Le premier a 4 colonnes et c'est là que nous verrons le code assembleur du programme. La première colonne affiche les adresses sur lesquelles se trouve la ligne. La deuxième colonne affiche le vidage hexadécimal de l'instruction. Vous remarquerez qu'il existe certains modèles. Par exemple, "PUSH 0" est toujours représenté par "6A 00". Lorsque vous utilisez des valeurs numériques entières comme arguments, l'instruction "PUSH" est toujours représentée par "6A", suivie de la représentation de l'argument. C'est parce que c'est ainsi que fonctionne le désassembleur : il génère un vidage hexadécimal du fichier et recherche des modèles. La troisième colonne n'est rien de moins que le code assembleur pour cette instruction. La quatrième colonne contient des commentaires de débogueur d'une grande valeur pour nous. Juste à côté de ce cadre, nous avons une zone ("Registres") qui montre les registres et les drapeaux, mis à jour pendant l'exécution de l'application. Ci-dessous, nous avons deux cadres. Le premier affiche la mémoire RAM utilisée par l'application, octet par octet. Avec trois colonnes, il affiche l'adresse, la représentation hexadécimale (8 octets/ligne) et la représentation ASCII. Le dernier cadre (mais très important) affiche l'état de la pile. La pile est souvent utilisée pour passer des paramètres (arguments) aux fonctions. Il comporte 3 colonnes, affichant l'adresse, la valeur et un commentaire important fait par le débogueur. Pour ouvrir un fichier binaire depuis Olly vous devez soit le faire glisser vers son icône, soit utiliser la commande "Ouvrir" (Fichier > Ouvrir). Soulignons que ce qui vous aidera maintenant, c'est votre propre curiosité. Créez de petits programmes, compilez-les et jouez-les à l'intérieur. Bonne chance dans tes études. Nous y voilà? Je vais maintenant le lancer en debug, cracker 2 pseudo-logiciels. Les deux pseudo-logiciels sont des crackme (spécialement conçus pour être crackés). Dans le premier, il y a une série statique, que nous devons connaître. Dans le second, la série n'est pas statique. Comme il demande également le nom, nous pensons que le numéro de série est calculé à partir de ces données. Je vais utiliser Olly Debugger.
Les deux crackme ont été développés par moi en C. Ils n'ont pas d'interface graphique en raison de la paresse. Vous pouvez les télécharger avec le code source Seuls les utilisateurs enregistrés et activés peuvent voir les liens., Cliquez ici pour vous inscrire... . Bien sûr, vous ne regarderez pas le code source avant de les battre, ou vous manquerez de plaisir (et c'est votre problème !). Série statique Il est impossible que cela existe toujours, mais pas improbable. De plus, nous devons commencer de la manière la plus simple. Ouvrons ensuite le fichier "estatico.exe". Une fenêtre apparaît en disant "Entrer série". Nous n'avons aucune idée de ce qu'est cette série, alors tapons n'importe quoi: "burger oil". Une fenêtre avec le titre "CrackMe" et le texte "Wrong!", une boîte de message Windows standard, est apparue. Il est très important de sauvegarder les types de messages d'erreur et les textes. Cliquons sur Ok et sortons du programme. Faites glisser le fichier "estatico.exe" devant l'icône OllyDbg. En peu de temps, il affichera notre programme, en Assemblage, dans le plus grand cadre (principal) de la fenêtre. Nous devons trouver ce message d'erreur, pour définir ce qui l'a causé. Cliquez avec le bouton droit sur le cadre principal > Rechercher > Toutes les chaînes de texte référencées. N'oubliez pas que ce n'est pas le seul moyen de trouver une erreur. Certains logiciels se ferment tout simplement. Vous pouvez utiliser "Tous les appels intermodulaires" et rechercher un ExitProcess. Mais c'est hors de propos maintenant. A noter qu'un tableau avec les textes présents dans le programme s'affiche. Vous en connaissez peut-être et d'autres non. Qu'est-ce qui était affiché dans le message d'erreur ? Notez que le texte que nous recherchons ("Mauvais!") y est indiqué comme suit : Code: ASCII "Faux !"
Double-cliquez sur cette chaîne. Voir qu'une ligne a été sélectionnée où nous avons trouvé notre texte dans les commentaires. A la fin, la fonction MessageBoxA est appelée. Intéressant... Juste après cela, nous voyons une MessageBox avec le texte "Correct!", ce que nous voulons. Mais qu'est-ce qui l'a causé ? On peut chercher un CMP ou un TEST, mais j'ai déjà dit que j'avais développé ce crackme en C. La fonction de comparaison utilisée en C est strcmp(). Lignes avant les MessageBoxes, nous voyons dans les commentaires (juste sous 'puts' - une fonction qui envoie du texte à la sortie - et gets() - une fonction qui prend la saisie au clavier) la fonction que nous recherchions (strcmp()). Maintenant, c'est simple : créons un point d'arrêt sur cette ligne où se trouve strcmp() et voyons ce qui se passe dans la mémoire de l'ordinateur. Un point d'arrêt (breakpoint) indique au débogueur que, lorsqu'il atteint cette partie du code, il interrompra l'exécution et sélectionnera la ligne où il se trouvait. Après une pause, vous pouvez utiliser les commandes Lecture/Pause de la barre d'outils pour reprendre l'exécution, ou Déboguer > Redémarrer dans la barre de menus pour relancer l'exécution. Pour créer un point d'arrêt, sélectionnez la ligne où se trouve strcmp, faites un clic droit > Point d'arrêt > Basculer ou appuyez simplement sur F2. Notez que l'adresse sera marquée dans une couleur différente.
Très bien? Cliquez sur le bouton de lecture de la barre d'outils et, dans la barre des tâches, consultez notre fenêtre d'application. Entrez n'importe quel numéro de série que vous pourrez reconnaître plus tard, tel que AAAAAAAAAAAAAAAAAAAAA. Notez que l'exécution a été interrompue et que la ligne avec notre point d'arrêt (strcmp) a été sélectionnée. Dans le cadre de la mémoire RAM, voyez à quel point nous avons trouvé intéressant : il y a deux chaînes (s1 et s2). L'un correspond à ce que nous avons tapé et l'autre a la valeur "SMURF". Fermez Olly (salut minimiser), ouvrez notre application et entrez notre série : SMURF. Résultat : « est le Série dynamique Maintenant, la chose pourrait devenir un peu compliquée. Généralement, lorsqu'on parle de série dynamique, il est difficile de générer une série et à la fin de la comparer à la série tapée - et c'est tout (même si vous pouvez le trouver dans les logiciels pour petites entreprises). Les tests sont généralement effectués avec la série, par exemple si le premier nombre est pair, si le deuxième caractère est une lettre de G à L, si le troisième nombre est un nombre premier, si vous divisez le quatrième avec le dernier nombre si vous obtenez un reste 3, et ainsi de suite... Mais ici, nous allons voir un exemple très simple, où un numéro de série est généré à partir du nom tapé et le compare avec le numéro de série tapé. Nous n'allons pas faire de keygen, après tout, ce tutoriel n'est qu'un début. Pour l'instant, prenons juste une série pour notre nom. Bien sûr, vous pouvez vous aventurer dans l'assembly généré et, avec une connaissance de base de C, comprendre la formule que j'utilise pour convertir un nom en série (et je le recommande). Mais, comme l'a dit Chuck Norris, "allons en morceaux". Exécutez le fichier "dynamic.exe". Notez qu'il demande un nom et un numéro de série. Entrez toutes les données. On nous a montré une MessageBox avec le titre "CrackMe" et le texte "Wrong!". Comme précédemment, faites un clic droit sur le cadre principal > Rechercher > Toutes les chaînes de texte référencées. Recherchez « Mauvais ! » et double-cliquez sur votre ligne. Notez que la fonction MessageBoxA n'est appelée qu'une seule fois. Au contraire, la valeur « Mauvais ! » ou "Correct !" est transféré dans une variable et envoyé dans l'appel de fonction. Vous voyez à quel point les commentaires sont importants ? Ne faisons pas les choses différemment cette fois. Recherchez un appel à la fonction strcmp juste avant la chaîne "Wrong!" et là créer un point d'arrêt. Exécutez le programme, entrez votre nom/pseudo et n'importe quel numéro de série. Lorsque vous interrompez la course, n'oubliez pas que vous avez le droit de savoir ce qui se passe dans la mémoire de votre ordinateur. Pour mon pseudo (0KaL), voir le numéro de série valide : Notez que tous les tirets ("-") saisis sont perdus. De plus, il ne demande que 16 caractères (sans compter les tirets), et le numéro de série valide en a 4 de plus. Comptez donc les 16 premiers caractères à l'envers. Notez qu'ils ont été omis du premier "S" au premier "w".
Séparez maintenant la série en groupes de 4 caractères, concaténés par des tirets. Résultat : 416b-403c-416b-403C. Il est très probable que le nom que vous avez tapé dans la série soit différent. Testons-le ? Écrivez-le sur un morceau de papier, fermez Olly, lancez le programme et testez :) Mais maintenant, expliquons un peu plus les lignes autour de « Mauvais ! » : Code:
00401643 |. E8 E8050000 APPEL ; |\strcmp 00401648 |. 85C0 ESSAI EAX,EAX ; | 0040164A |. 74 18 JE COURT dynamique.00401664 ; | 0040164C |. C74424 04 4731>MOV DWORD PTR SS :[ESP+4],dynamique.004031> ; | ASCII "Mauvais!" 00401654 |. 8D85 A8FAFFFF LEA EAX,DWORD PTR SS :[EBP-558] ; | 0040165A |. 890424 MOV DWORD PTR SS:[ESP],EAX ; | 0040165D |. E8 EE050000 APPEL ; \strcpy 00401662 |. EB 16 JMP COURT dynamique.0040167A 00401664 |> C74424 04 4F31>MOV DWORD PTR SS:[ESP+4],dynamic.004031>; |ASCII "Correct !" 0040166C |. 8D85 A8FAFFFF LEA EAX,DWORD PTR SS :[EBP-558] ; | 00401672 |. 890424 MOV DWORD PTR SS:[ESP],EAX ; | 00401675 |. E8 D6050000 APPEL ; \strcpy
Nous voyons d'abord un appel à la fonction strcmp(), qui compare si deux chaînes sont égales. En C, si les chaînes sont égales, la fonction renvoie zéro (0). En Assemblage, le résultat est joué dans le registre "EAX". Nous voyons donc un TEST EAX,EAX, suivi d'un Jump si égal. Autrement dit, si TEST EAX,EAX (résultat de strcmp()) est égal à 0, passez à l'adresse 00401664. Sinon, continuez normalement. Mais qu'y a-t-il à l'adresse 00401664 ? Notez qu'il a également été copié dans le morceau de code ci-dessus, avec le commentaire 'ASCII "Correct!"'. Mais qu'en est-il, au lieu de libérer un nom de série valide + permettant à l'utilisateur de taper n'importe quoi, faisant en sorte que le programme accepte n'importe quel numéro de série, sans le valider ? Nous verrons maintenant. Patch Bien que certains pensent que comprendre des formules et publier des serials ou des keygens sont les moyens les plus cool de promouvoir le cracking, j'ai particulièrement toujours aimé l'art de changer les exécutables en manipulant leurs instructions. Cette partie de chacun. Ce que nous allons voir ici n'est pas difficile du tout. Nous avons vu plus haut que nous avons un conditionnel TEST suivi d'un JE. Le JE (Jump if Equal) dans ce cas indique l'adresse à laquelle le processeur sautera, si le conditionnel précédent est vrai. Alors on peut faire autrement : au lieu de sauter si c'est pareil, sauter quand même. Double-cliquez sur la ligne 0040164A (la ligne contenant le JE). Dans la fenêtre qui apparaît, il y a une zone de texte avec les éléments suivants : Code: JE COURT 00401664
Notre raisonnement : ne sautez pas si égal. Sautez quand même. De cette façon, remplacez "JE" par "JMP" (Jump - mais indépendant de If Equal), en laissant : Code: JMP COURT 00401664
Correct? Si la case "Remplir avec les NOP" n'est pas cochée, cochez-la. Il sert à compléter les bits restants avec "NOP" (pas d'opération - ne rien faire). Cliquez sur le bouton Assemblage et enregistrons maintenant le fichier.
Pour cela, faites un clic droit sur le cadre principal > Copier dans l'exécutable > Toutes les modifications. Cliquez sur "Tout copier". Une autre fenêtre apparaîtra avec le code assembleur du programme. Faites un clic droit dessus > Enregistrer le fichier. Sélectionnez où enregistrer votre nouveau fichier. Exécutez le fichier que vous venez d'enregistrer. Il vous demandera le nom et le numéro de série. Tapez n'importe quoi et, regardez comme c'est fantastique !, il a accepté ! ++Comme je l'ai dit, ce n'était qu'une introduction de base. Continuez à rechercher et à apprendre. Brossez-vous les dents après chaque repas et gardez vos mains propres. Au suivant! Ce matériel peut être partagé, à condition qu'un crédit approprié soit accordé.