Crackme V1.0 de CoOlViPeR
By Christal
|
CoOlViPeR Crackme 1 :0040AEBD MOV AH,43 :0040AEBF INT 68 :0040AEC1 CMP AX,F386 :0040AEC5 JNZ 0040AECD :0040AEC7 PUSH 00000000 :0040AECC RET :0040AECD PUSH 0040AD70 :0040AED2 RET Avec l’inconvénient d'un
PUSH 00000000 si SoftIce est détecté, qui fait planter le programme. En analysant un minimun l'écran que SoftIce à ouvert lors du plantage de procdump (FAULT ON):
ProcDump va planter parce que la valeur qu'il recoit en 004056B6 ne correspond à rien tant que l'application cible n'est pas en cours d'exécution. Un "d EDI" en 004056A9 va montrer que le programme pointe sur le début du MZ Header: 00000000 4D5A 9000 0300 0000 0400 0000 FFFF 0000 B800 0000 0000 0000 MZ...................... 00000018 4000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 @....................... 00000030 0000 0000 0000 0000 0000 0000 1B29 0000 0E1F BA0E 00B4 09CD .............).......... 00000048 21B8 014C CD21 5468 6973 2070 726F 6772 616D 2063 616E 6E6F !..L.!This program canno Et qu'à l'adresse ESI (0058C000) + 3C il y a un 291B (EAX). Ce champ est indispensable pour Windows, il s'agit du DWORD e_lfanew, le dernier du MZ Header, indiquant à quel offset le PE Header de l'exécutable est situé. Nomalement, cette valeur devrait être C0, et pointer sur l'offset où se trouve PE (le début du PE Header), que l'on trouve quelques lignes plus bas: 000000C0 5045 0000 4C01 0000 9869 0342 0000 0000 0000 0000 E000 0F00 PE..L....i.B............
000001B0 0000 0000 0000 0000 2E74 6578 7400 0000 0080 0000 0010 0000 .........text........... 000001C8 0000 0000 0004 0000 0000 0000 0000 0000 0000 0000 8000 00E0 ........................ 000001E0 2E64 6174 6100 0000 0020 0000 0090 0000 0020 0000 0004 0000 .data.... ....... ...... 000001F8 0000 0000 0000 0000 0000 0000 4000 00E0 2E72 7372 6300 0000 ............@....rsrc... 00000210 0010 0000 00B0 0000 0006 0000 0024 0000 0000 0000 0000 0000 .............$.......... 00000228 0000 0000 4000 00C0 0000 0000 0000 0000 0000 0000 0000 0000 ....@................... Epluchons un peu: Section Header (section .text) BYTE Name[IMAGE_SIZEOF_SHORT_NAME] ( 2E74 6578 7400 ) DWORD PhysicalAddress DWORD VirtualSize ( 0080 0000 ) DWORD VirtualAddress ( 0010 0000 ) DWORD SizeOfRawData ( 0000 0000 ) DWORD PointerToRawData ( 0004 0000 ) DWORD PointerToRelocations ( 0000 0000 ) DWORD PointerToLinenumbers ( 0000 0000 ) WORD NumberOfRelocations ( 0000 ) WORD NumberOfLinenumbers ( 0000 ) DWORD Caracteristiques ( 8000 00E0 ) -> E00000080 Ici, rien d'anormal... Alors que peut il bien y avoir à l'offset 291B, sachant que sans les informations du header, l'application plantera immédiatement. Dans le cas de ce crackme, le PE Header a été détourné vers l'offset 291B, à fin du code: Name: VS: 00008000 VA: 00001000 RS: 00000000 RA: 00000400 C: C0000040 Name: VS: 00002000 VA: 00009000 RS: 00002000 RA: 00000400 C: C0000040 Name: VS: 00001000 VA: 0000B000 RS: 00000600 RA: 00002400 C: C0000040 Pas de Nom de section, le minimum de chez minimum, mais on retrouve des points communs 0000291B 50 4500 004C 0103 0098 6903 4200 PE..L....i.B. etc... 00002A00 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 ........................ 00002A18 0000 0000 8000 0000 1000 0000 0000 0000 0400 0000 0000 0000 ........................ 00002A30 0000 0000 0000 0040 0000 C000 0000 0000 0000 0000 2000 0000 ....... ............ ... 00002A48 9000 0000 2000 0000 0400 0000 0000 0000 0000 0000 0000 0040 .... ..................@ 00002A60 0000 C000 0000 0000 0000 0000 1000 0000 B000 0000 0600 0000 ........................ 00002A78 2400 0000 0000 0000 0000 0000 0000 0040 0000 C000 0000 0000 $..............@........ 00002A90 0000 0000 0000 ...... Pour résumer, CoOlViPeR a détourné l'adresse de début du PE Header, et a créé un nouvel Header qu'il a placé à la fin de la section .rsrc, puis il a supprimé le nom des sections et modifié les caractéristiques de celles ci: Généralement, pour empêcher le break et le désassemblage de la section, il suffit de mettre les caractéristiques suivantes : 0x00000040 IMAGE_SCN_CNT_INITIALIZED_DATA 0x40000000 IMAGE_SCN_MEM_READ OR 0x80000000 IMAGE_SCN_MEM_WRITE -------------------------------------------- 0xC0000040 En inversant le principe: Chaque caractéristique à une valeur. Si vous voulez avoir deux caractéristiques,
vous faites : caract1 OR caract2 ... 0x00000020 IMAGE_SCN_CNT_CODE 0x20000000 IMAGE_SCN_MEM_EXECUTE 0x40000000 IMAGE_SCN_MEM_READ OR 0x80000000 IMAGE_SCN_MEM_WRITE ----------------------------------- 0xE0000020 Wdasm va rencontrer les mêmes difficultés que ProcDump face à de telles modifications. Seul le lancement du CrackMe permet de rendre "actif" le détournement du PE. Mais est ce vraiment satisfaisant? Pas totalement. Le mieux est encore de remettre de l'ordre dans le Chaos de CoOlViPer.... Et à commencer par rediriger le programme vers son VRAI PE Header, puis (et votre oeil exercé ne l'aura pas loupé), REINSCRIRE LE NOMBRE DE SECTIONS du CRACKME, à savoir 03... 00000030 0000 0000 0000 0000 0000 0000 C0000 0000 0E1F BA0E 00B4 09CD .............)......... et 000000C0 5045 0000 4C01 0300 9869 0342 0000 0000 0000 0000 E000 0F00 PE..L....i.B............ Mais il y a bien plus simple, il suffit d'utiliser un autre Dumper comme ProcInfo,
et le tour est joué... :0040AD70 60 PUSHAD :0040AD71 BE00904000 MOV ESI,00409000 :0040AD76 8DBE0080FFFF LEA EDI,[ESI+FFFF8000] :0040AD7C 57 PUSH EDI :0040AD7D 83CDFF OR EBP,-01 :0040AD80 EB10 JMP 0040AD92 Seconde étape, l'adresse 0040AD92, en plein dans le décompresseur: :0040AD92 8B1E MOV EBX,[ESI] :0040AD94 83EEFC SUB ESI,-04 :0040AD97 11DB ADC EBX,EBX :0040AD99 72ED JB 0040AD88 etc... Plutôt qu'un tracing step by step qui peut se révéler fastidieux, une rapide dégringolade avec l'ascenseur dans la fenêtres du code, permet de trouver rapidement le POPAD de fin de décompression: :0040AEB1 FF96A0A40000 CALL [ESI+0000A4A0] :0040AEB7 61 POPAD :0040AEB8 E95F66FFFF JMP 0040151C Chose étonnante, on trouve un saut direct vers... :0040151C 60 PUSHAD :0040151D BE00104000 MOV ESI,00401000 :00401522 BF1B154000 MOV EDI,0040151B :00401527 BA52154000 MOV EDX,00401552 :0040152C 8BCA MOV ECX,EDX :0040152E 32FF XOR BH,BH :00401530 FEC7 INC BH Ce qui ressemble à une nouvelle boucle de décompression, avec un beau
0040100, Entry point classique des programmes raisonnables... :0040154B 61 POPAD :0040154C 68DD144000 PUSH 004014DD :00401551 C3 RET Il est juste quelques lignes plus bas... :004014DD 60 PUSHAD :004014DE BE00104000 MOV ESI,00401000 :004014E3 BFDC144000 MOV EDI,004014DC :004014E8 BA13154000 MOV EDX,00401513 :004014ED 8BCA MOV ECX,EDX :004014EF 32FF XOR BH,BH :004014F1 FEC7 INC BH etc... :0040150C 61 POPAD :0040150D 689E144000 PUSH 0040149E :00401512 C3 RET Avec une reproduction du schéma précèdent. :0040149E 60 PUSHAD :0040149F BE00104000 MOV ESI,00401000 :004014A4 BF9D144000 MOV EDI,0040149D :004014A9 BAD4144000 MOV EDX,004014D4 :004014AE 8BCA MOV ECX,EDX :004014B0 32FF XOR BH,BH :004014B2 FEC7 INC BH etc... :004014CD 61 POPAD :004014CE 6800104000 PUSH 00401000 :004014D3 C3 RET On prend les mêmes, et on recommence...
Dans les deux cas, après remplacement de la valeur de l'Entry Point d'origine par 1000 (l'ImageBase étant 00400000+1000 = 401000, l'Entry Point d'origine du Crackme), le résultat sera runnable et décompilable. :00401000 6A00 PUSH 00 > Original Entry Point :00401002 E861040000 CALL KERNEL32!GetModuleHandleA :00401007 A360304000 MOV [00403060],EAX :0040100C 6A0A PUSH 0A :0040100E 6A00 PUSH 00 :00401010 6A00 PUSH 00 :00401012 FF3560304000 PUSH DWORD PTR [00403060] :00401018 E806000000 CALL 00401023 :0040101D 50 PUSH EAX :0040101E E83F040000 CALL KERNEL32!ExitProcess Passons maintenant au crack du programme. :00401267 6800010000 PUSH 00000100 ; maxi 100h caractères à saisir :0040126C 6868314000 PUSH 00403168 ; adresse de stockage du name :00401271 6A69 PUSH 69 ; IDentificateur du champ 1 :00401273 FF355C304000 PUSH DWORD PTR [0040305C] ; handle de la fenêtre-objet :00401279 E89C010000 CALL GetDlgItemTextA :0040127E 6800010000 PUSH 00000100 :00401283 6864304000 PUSH 00403064 ; adresse de stockage du serial :00401288 6A6A PUSH 6A ; ID du champ 2 :0040128A FF355C304000 PUSH DWORD PTR [0040305C] :00401290 E885010000 CALL USER32!GetDlgItemTextA Ensuite vous trouvez les habituels contrôles: :00401295 6868314000 PUSH 00403168 ; Name :0040129A E8E1010000 CALL KERNEL32!lstrlen ; calcul de sa longueur :0040129F EB03 JMP 004012A4 ; héhé! Ben vi, le code a été "polymorphé", c'est un CCA
(code changeant d'apparence), sensé nous pourrir la vie... c XOR ! = B h XOR 05 = m r XOR 85 = F7 les trois caractères obtenus sont stocké dans l'ordre inverse: F7mB :004012F3 ADD ESI,03 > pointe sur le quatrième caractère (i) :004012F6 MOV AL,[ESI] > charge le caractère dans al :004012F8 CMP AL,00 > vérifie que ce n'est pas le dernier caractère de la string :004012FA JZ 00401309 > sinon Go_Out :004012FC XOR AL,21 > XOR ce caractère avec 21 :004012FE MOV [EDI],AL > passe au caractère suivant 3- le résultat du premier XOR (7FmB) est manipulé par SHL EAX, 1C (0000007F -> 70000000) et par SHR eax, 1C (70000000 -> 00000007), puis additionné avec 41 (lettre A) pour obtenir un caractère alphabétique. En fait le 00000007 obtenu sert à pointer dans l'alphabet. Le résultat final donne HNC. MOV AL,[ESI] > la string 7FmB est stockée dans ESI. AL = 7F SHL EAX,1C > 0000007F -> 70000000 SHR EAX,1C > 70000000 -> 00000007 ADD EAX,41 > 07 + 41 = 48 (la lettre H) MOV [EDI],AL > stockage du résultat obtenu dans l'adresse pointée par EDI 4- même chose pour la seconde chaine XORée. Cette fois ci j'obtiens
ICFAN :0040134E PUSH 00403474 > 1ere chaîne créée précédemment :00401353 PUSH 0040367C > adresse de stockage de la nouvelle chaine :00401358 CALL KERNEL32!lstrcat :0040135D PUSH 00403020 > "-" :00401362 PUSH 0040367C > adresse de stockage du résultat :00401367 CALL KERNEL32!lstrcat :0040136C PUSH 00403578 > 2eme chaîne créée précédemment :00401371 PUSH 0040367C > adresse de stockage du résultat :00401376 CALL KERNEL32!lstrcat Juste après, caché dans le code polymorphe, vous trouverez en 00401380 un joli: PUSH 00403064 > le serial entré PUSH 0040367C > la chaîne concaténée CALL lstrcmp > comparaison CMP EAX,00 > si une différence a été trouvée JNZ 004013B7 > Beggar Off mais si votre serial est Good, vous aurez droit à une boite de message "Good Serial": :004013A0 PUSH 00403014 > titre de la messageBox :004013A5 PUSH 0040367C > Good message :004013AA PUSH DWORD PTR [EBP+08] > handle :004013AD CALL USER32!MessageBoxA L'option que j'ai retenue, est de pousser sur la pile l'adresse du bon code à
la place du serial entré (en 00401380), et ce même good serial à la place du message en 004013A5,
histoire d'utiliser le crackme pour qu'il retourne lui même le serial à entrer. :0040AE2C MOV EAX,[EDX] > charge le contenu de EDX :0040AE2E ADD EDX,04 > passe au DWORD suivant :0040AE31 MOV [EDI],EAX > stock EAX à l'adresse pointée par EDI :0040AE33 ADD EDI,04 > passe au DWORD suivant 1ère remarque: le contenu
de l'adresse EDX est déplacé de 12 octets, pour atterrir dans [EDI] :0040AEBD MOV AH,43 :0040AEBF INT 68 :0040AEC1 CMP AX,F386 :0040AEC5 JNZ 0040AECD :0040AEC7 PUSH 00000000 :0040AECC RET :0040AECD PUSH 0040AD70 :0040AED2 RET :0040AED3 ADD [EAX],AL > place disponible :0040AED5 ADD [EAX],AL :0040AED7 ADD [EAX],AL :0040AED9 ADD [EAX],AL En fait de compresseur, il laisse pas mal de place derrière lui, les ADD
[EAX],AL (00 00), alors que c'est justement son rôle que de les supprimer. :0040AE2E E9A0000000 JMP 0040AED3 > vers Patch1 :0040AE33 83C704 ADD EDI,04 > adresse de retour :0040AE36 83E904 SUB ECX,04 5- écriture du Patch1. Son rôle sera de détourner le programme immédiatement avant le saut vers l'EIP pour qu'il se rendre au Patch2. :0040AED3 81FACE144000 CMP EDX,004014CE > l'adresse attendue en fin de décompression :0040AED9 7C12 JL 0040AEED > si EDX est inférieur, on saute :0040AEDB 81FAD6144000 CMP EDX,004014D6 > et idem si EDX est supérieur à l'adresse :0040AEE1 7F0A JG 0040AEED > visée + 8 bytes :0040AEE3 C705CF144000FEAE4000MOV DWORD PTR [004014CF],0040AEFE > on patch :0040AEED 83C204 ADD EDX,04 > restoration des octets modifiés par le JMP :0040AEF0 8907 MOV [EDI],EAX > en 0040AE2E :0040AEF2 E93CFFFFFF JMP 0040AE33 > et on quitte le Patch1 -> vers adresse de retour Contrôle du résultat: :004014CD POPAD :004014CE PUSH 0040AEFE :004014D3 RET :004014D4 PUSH EAX C'est Good... :0040AEFE C705811340007C364000MOV DWORD PTR [00401381],0040367C :0040AF08 C705A61340007C364000MOV DWORD PTR [004013A6],0040367C :0040AF12 6800104000 PUSH 00401000 > retour à la normale :0040AF17 C3 RET > et GO OEP Petit essai... |
Bonne Journée
PS: Le KeyGenerateur qui accompagne ce programme est équipé d'un neutraliseur pour l'Anti SoftIce, mais à la condition qu'ils se trouvent l'un est l'autre dans le même répertoire.