n0p3x Crackme
By Christal
Infections:
Le crackme de n0p3x est compressé. Pour noyer le poisson, les sections ont été renommées, mais un outil comme Get Type n'est pas dupe: TIT0 VS: 00007000 VA: 00001000 RS: 00000000 RA: 00000400 C: E0000080 TIT1 VS: 00001000 VA: 00008000 RS: 00000200 RA: 00000400 C: C0000040 TIT2 VS: 00001000 VA: 00009000 RS: 00000C1A RA: 00000600 C: 60000020 et il détecte de suite le packeur utilisé: Portable executable (starting at 256 for 4378 bytes) UPX est particulièrement facile à reverser, poue peu que vous ayez
changé le E00000080 en
E00000020: :00409000 60 PUSHAD > Entry Point :00409001 E800000000 CALL 00409006 :00409006 83CDFF OR EBP,-01 Il n'y a plus qu'à trouver où est le POPAD: :00409197 61 POPAD :00409198 EBFE JMP 00401000 remplacer le jmp 00401000 (Entry Point classique de tout bon programme) par un JMP
EIP (pour que l'application boucle sur elle-même), et utiliser l'excellent ProcInfo de TeeJi pour obtenir
un dump runnable, désassemblable et avec Strings Data. :0042B839 CALL KERNEL32!GetModuleHandleA :0042B83E PUSH EAX :0042B83F CALL [ESI+18] > apparition du Nag (via MessageBoxA) Début du call [ESI+18]: :0040136F PUSH EBP :00401370 MOV EBP,ESP :00401372 PUSH EBX :00401373 MOV EBX,00402074 > \\.\SICE :00401378 PUSH 00001000 > Type du bouton de la Box :0040137D LEA EAX,[EBX+00000229] > Titre "Warning" :00401383 PUSH EAX > poussé sur la pile :00401384 LEA EDX,[EBX+000001EC] > message "This is a shareware..." :0040138A PUSH EDX > poussé sur la pile :0040138B PUSH 00 > attribut d'affichage de la box :0040138D CALL USER32!MessageBoxA > le nag apparaît Un "D 402074" va nous donner pas mal d'indications: :00402260 54 68 69 73 20 69 73 20-61 20 73 68 61 72 65 77 This is a sharew :00402270 61 72 65 20 76 65 72 73-69 6F 6E 20 6F 66 20 74 are version of t :00402280 68 69 73 20 70 72 6F 67-72 61 6D 2C 20 70 6C 65 his program, ple :00402290 61 73 65 20 72 65 67 69-73 74 65 72 00 57 41 52 ase register.WAR :004022A0 4E 49 4E 47 00 53 6F 66-74 49 43 45 20 69 73 20 NING.SoftICE is :004022B0 6C 6F 61 64 65 64 2E 20-50 6C 65 61 73 65 20 64 loaded. Please d :004022C0 6F 20 6E 6F 74 20 70 69-72 61 74 65 20 74 68 69 o not pirate thi :004022D0 73 20 73 6F 66 74 77 61-72 65 2E 00 57 41 52 4E s software..WARN :004022E0 49 4E 47 00 53 6F 66 74-49 43 45 20 69 73 20 6C ING.SoftICE is l :004022F0 6F 61 64 65 64 2E 20 50-6C 65 61 73 65 20 64 6F oaded. Please do :00402300 20 6E 6F 74 20 70 69 72-61 74 65 20 74 68 69 73 not pirate this :00402310 20 73 6F 66 74 77 61 72-65 2E 00 57 41 52 4E 49 software..WARNI :00402320 4E 47 00 59 6F 75 27 72-65 20 6B 65 79 66 69 6C NG.You're keyfil :00402330 65 20 69 73 20 69 6E 76-61 6C 69 64 2E 20 50 6C e is invalid. Pl :00402340 65 61 73 65 20 6F 62 74-61 69 6E 20 61 20 63 6F ease obtain a co :00402350 72 72 65 63 74 20 6B 65-79 66 69 6C 65 20 66 72 rrect keyfile fr :00402360 6F 6D 20 79 6F 75 72 20-73 6F 66 74 77 61 72 65 om your software :00402370 20 76 65 6E 64 6F 72 00-57 41 52 4E 49 4E 47 00 vendor.WARNING. Le programme est équipé d'au moins un AntiSice, et nécessite
une KeyFile. :00401392 CALL 0040107C > test de la présence de SICE :00401397 TEST EAX,EAX > si EAX = de 0 :00401399 JZ 004013B7 > évite le MessageBox :0040139B PUSH 00001000 > type de bouton :004013A0 LEA ECX,[EBX+00000268] > charge string Titre :004013A6 PUSH ECX > pousse tître :004013A7 LEA EAX,[EBX+00000231] > charge string Message :004013AD PUSH EAX > pousse message :004013AE PUSH 00 > attribut de la box :004013B0 CALL USER32!MessageBoxA > "SoftIce Loaded" :004013B5 JMP 00401414 Dans le call 0040107C, la présence de SoftIce est détectée par un MeltIce: :0040107C PUSH EBP :0040107D MOV EBP,ESP :0040107F PUSH 00 > handle du fichier :00401081 PUSH 00000080 > attributs du fichier :00401086 PUSH 03 > type de création :00401088 PUSH 00 > pointeur lpSecurityAttributes :0040108A PUSH 03 > dwShareMode :0040108C PUSH C0000000 > mode d'accès (read-Write) :00401091 PUSH 00402074 > pousse SICE :00401096 CALL KERNEL32!CreateFileA > recherche :0040109B CMP EAX,-01 > -1 si non trouvé :0040109E JZ 004010AD un "D 00402074" va encore nous donner des tuyaux intéressants: :00402074 5C 5C 2E 5C 53 49 43 45-00 5C 5C 2E 5C 4E 54 49 \\.\SICE.\\.\NTI :00402084 43 45 00 52 65 67 69 73-74 65 72 2E 64 61 74 00 CE.Register.dat. Pour se débarasser définitivement de l'antiSice, il suffit de remplacer les quatre caractères de SICE par n'importe quoi d'autre. Les trois tests de détection effectués par le crackme font tous référence à l'adresse 00402074, mais il ne faut pas oublier que le programme est packé. Un BPM 00402074 va nous permettre de savoir à quel moment cette adresse est décompréssée par UPX: :00409028 8A06 MOV AL,[ESI] > ESI = 00409697 :0040902A 46 INC ESI > byte suivant :0040902B 8807 MOV [EDI],AL > al mis dans 00402074 :0040902D 47 INC EDI > byte suivant :0040902E 01DB ADD EBX,EBX > vérifie état décompression :00409030 7507 JNZ 00409039 > OK ? -> pas de décompression :00409032 8B1E MOV EBX,[ESI] > chaîne à traiter dans EBX :00409034 83EEFC SUB ESI,-04 > décrémente ESI d'un DWORD :00409037 11DB ADC EBX,EBX > décompression :00409039 72ED JB 00409028 > boucle Il faut bien distinguer que UPX est un compresseur, pas un crypteur! Son rôle est de diminuer la taille de l'exécutable, pas de planquer du code... .00409690: FB 5F 61 D1-F9 58 5C 5C-2E 5C 53 49-43 45 08 FF ¹_aШX\\.\SICE .004096A0: FF FF 1D 4E-54 09 52 65-67 69 73 74-65 72 2E 64 NT Register.d En 00409690, le code est en clair et accéssible par un HexEditeur. C'est dans cette zone mémoire qu'UPX va récupérer les codes compressé et les déplacer vers la zone mémoire qui sera utilisée par l'exécutable. Tous les bytes qu'UPX aura lu lors de son application ne sont pas obligatoirement compressable, et il est normal que des portion du code se retrouve en clair. Morceau de chance, SICE font partie de ceux la. Outre la présence de SoftIce pour Window 98, le programme va aussi chercher
si le crackme ne tourne pas sur une version NT. Bien plus intéressant, vous remarquerez le "register.dat"... :004013B7 CALL 004010B1 > détection NTICE :004013BC TEST EAX,EAX > toujours pas un MeltIce :004013BE JZ 004013DC > good boy -> Go on :004013C0 PUSH 00001000 > type des boutons :004013C5 LEA EDX,[EBX+000002A7] > tître :004013CB PUSH EDX :004013CC LEA ECX,[EBX+00000270] > message :004013D2 PUSH ECX :004013D3 PUSH 00 > attribut de la boite :004013D5 CALL USER32!MessageBoxA > "softIce detected" :004013DA JMP 00401414 Il faut reconnaître que les Crackme's ont du bon, comme ce sont de minuscules programmes, tout est sous la main... :004013DC CALL 004010FC > traitement du fichier "register.dat" :004013E1 TEST EAX,EAX > si absent, ou invalide :004013E3 JZ 004013FA > fermeture du programme La recherche et le traitement de "Register.dat" est assez facile à suivre: :004010FC PUSH EBP :004010FD MOV EBP,ESP :004010FF ADD ESP,-38 :00401102 PUSH EBX > pousse \\.\SICE :00401103 PUSH ESI :00401104 PUSH 00 :00401106 PUSH 00402087 > pousse "register.dat" :0040110B CALL 004017A6 > et en cherche la présence (dans cw3220.dll) :00401110 ADD ESP,08 :00402087 52 65 67 69 73 74 65 72-2E 64 61 74 00 57 68 79 Register.dat.Why :00402097 20 64 69 64 6E 27 74 20-6E 30 70 33 78 20 75 73 didn't n0p3x us :004020A7 65 20 61 20 6D 6F 72 65-20 64 69 66 66 69 63 75 e a more difficu :004020B7 6C 74 20 6B 65 79 66 69-6C 65 20 6D 65 74 68 6F lt keyfile metho :004020C7 64 3F d? La phrase qui suit Register.dat est pour le moins étrange. Nous verrons ce qu'il en est dans un instant. :00401113 MOV EBX,EAX > résultat dans EBX :00401115 INC EAX > si était égale à -1 :00401116 JZ 00401145 > fichier non trouvé :00401118 PUSH 35 :0040111A LEA EDX,[EBP-38] :0040111D PUSH EDX :0040111E PUSH EBX :0040111F CALL 004017AC > vers cw3220.dll :00401124 ADD ESP,0C :00401127 MOV ESI,EAX :00401129 INC EAX :0040112A JZ 00401145 :0040112C PUSH 00402094 > pousse "Why didn't n0px3..." :00401131 LEA EDX,[EBP-38] > charge texte trouvé dans register.dat :00401134 PUSH EDX > et le pousse sur la pile :00401135 CALL lstrcmp > compare les deux chaînes :0040113A TEST EAX,EAX > si OK, valeur de retour est un pointeur :0040113C JNZ 00401145 > sinon EAX =00 -> Go Out :0040113E MOV EAX,00000001 > Flag = 01 -> Good Boy :00401143 JMP 0040114E > et sortie :00401145 PUSH EBX :00401146 CALL 00401782 :0040114B POP ECX :0040114C XOR EAX,EAX > Flag = 00 -> Bad Boy :0040114E POP ESI :0040114F POP EBX :00401150 MOV ESP,EBP :00401152 POP EBP :00401153 RET La suite est nettement plus amusante, si la comparaison entre la string "Why didn't..." et celle trouvée dans le fichier Register.dat (que vous aurez créé pour l'occasion) donne une valeur différente de NULL (valeur retournée en cas d'échec), le Crackme va ouvrir une boite d'enregistrement à trois champs: Name, Company et Serial: :004013E5 PUSH 00 > valeur d'initialisation :004013E7 PUSH 00401301 > pointe vers dialogBox Proc :004013EC PUSH 00 > handle de la fenêtre propiétaire :004013EE PUSH 01 > identifie Template de la DialogBox :004013F0 PUSH DWORD PTR [EBP+08] > handle Instance de l'application :004013F3 CALL USER32!DialogBoxParamA > création d'une Modal DialogBox :004013F8 JMP 00401414 > saute par dessus routine Pas Glop! Une grande partie du traitement du serial va se faire via des allers/retours dans
le fichier cw3220.dll. :0040127C LEA EAX,[EBP-14] > Name :0040127F PUSH EAX > poussé sur la pile :00401280 CALL KERNEL32!lstrlen > calcul la longueur :00401285 MOV EBX,EAX > met en mémoire :00401287 LEA EAX,[EBP-28] > Company :0040128A PUSH EAX > poussé sur la pile :0040128B CALL KERNEL32!lstrlen > calcul la longueur :00401290 IMUL EBX,EAX > multiplie les 2 longueurs :00401293 ADD EBX,000F3EA9 > ajoute 0F3EA9 (clé) :00401299 MOV EAX,EBX > met en mémoire :0040129B PUSH 0A > pousse le diviseur :0040129D LEA EDX,[EBP-70] > @ où est stockée la clé :004012A0 PUSH EDX > pousse @ de la clé :004012A1 PUSH EAX > pousse la clé :004012A2 CALL 0040179A > Let's go... Nous avons donc un calcul du nombre de caractères qui composent le Name et
la Company. Ces deux valeurs sont multipliées entres elles, puis n0p3x y rajoute 0F3EA9. Cette valeur va
être stockée de deux façons: principalement dans un des registres (ils se passent le tour...)
et dans une adresse [mémoire]. 0A qui est poussé en 0040129B va jouer le rôle de diviseur... :0040124B PUSH EAX > pousse serial :0040124C CALL KERNEL32!lstrlen > calcul longueur :00401251 CMP EAX,09 > compare avec 9 :00401254 JGE 00401275 > saute si > ou égal :00401256 PUSH 00001000 :0040125B LEA EDX,[ESI+000000F9] > titre :00401261 PUSH EDX :00401262 LEA ECX,[ESI+000000B9] > message :00401268 PUSH ECX :00401269 PUSH 00 :0040126B CALL USER32!MessageBoxA > "the serial number you have entered..." :00401270 JMP 004012FB Le serial doit donc comporter 9 caractères minimum, sinon vous aurez une
boite de message Pas Glop! :0042573B MOV EAX,ESI > clé dans EAX :0042573D XOR EDX,EDX > ré-initialise EDX (le reste) :0042573F DIV EDI > clé / 0A = clé 2 -> reste dans EDX DIV EDI va diviser EDI par 0A. La partie entière (clé 2) va être stockée dans ESI, et le reste dans EDX. C'est ce reste qui va générer un caractère du serial. :00425741 MOV [ECX],DL > stock le reste dans [mémoire] :00425743 INC ECX :00425744 MOV EAX,ESI > EAX = partie entière :00425746 XOR EDX,EDX > réinitialise EDX (reste) :00425748 DIV EDI > clé 2 / 0A = clé 3 -> reste dans EDX :0042574A MOV ESI,EAX > ESI = partie entière :0042574C TEST EAX,EAX > si EAX = 0 -> plus de partie entière :0042574E JNZ 0042573B > sinon -> boucle :00425750 JMP 00425769 > EAX =0 :00425752 DEC ECX :00425753 MOV AL,[ECX] > ECX pointe sur le bon serial Ce serial est stocké sous la forme 07 03 01 09 09 09. Les valeurs calculées par le reste de la division sont stockées en "sens inverse" à l'adresse pointée par ECX (d'ou le dec ECX). Al va recevoir cette valeur, un chiffre hexédécimal inférieur à 10 (puisque c'est un reste!). :00425755 CMP AL,0A > si plus de 9 caractères ?! :00425757 JGE 00425761 > traitement particulier :00425759 ADD EAX,30 > ajoute 30h Le fait d'ajouter 30h au reste va "transformer" un chiffre hexa en sa correspondance ASCII. :0042575C MOV [EBX],AL > stock valeur ASCII dans EBX :0042575E INC EBX > et pointe sur caractère suivant :0042575F JMP 00425769 :00425761 ADD AL,[EBP+18] > NA :00425764 ADD AL,F6 > NA :00425766 MOV [EBX],AL > NA :00425768 INC EBX > NA :00425769 LEA EDX,[EBP-24] :0042576C CMP ECX,EDX > tous les caractères ont été traités? :0042576E JNZ 00425752 > non -> boucle Le nombre de caractères à traiter est de 6. Cette valeur est liée
à l'addition du résultat de la multiplication de la taille du Name et du Serial, avec 0F3EA9. Comme
le bon serial est basé sur une division par 10, il ne peut pas y en avoir plus, d'ou un traitement particulier
destiné à gérer les erreurs. :004012AA LEA ECX,[ESI+00000102] > pointe sur string :-) :004012B0 PUSH ECX > pousse adresse sur la pile :004012B1 LEA EAX,[EBP-70] > pointe sur adresse serial :004012B4 PUSH EAX > poussé sur la pile :004012B5 CALL KERNEL32!lstrcat > concaténation des 2 strings Voici ce que l'on trouve en [ESI+0102] = 00402176: :00402126 52 4E 49 4E 47 21 00 54-68 65 20 73 65 72 69 61 RNING!.The seria :00402136 6C 20 6E 75 6D 62 65 72-20 79 6F 75 20 68 61 76 l number you hav :00402146 65 20 65 6E 74 65 72 65-64 20 69 73 20 6E 6F 74 e entered is not :00402156 20 6F 66 20 74 68 65 20-63 6F 72 72 65 63 74 20 of the correct :00402166 6C 65 6E 67 74 68 00 57-41 52 4E 49 4E 47 21 00 length.WARNING!. :00402176 3A 2D 29 00 43 6F 6E 67-72 61 74 75 6C 61 74 69 :-).Congratulati :00402186 6F 6E 73 2E 20 42 65 20-73 75 72 65 20 74 6F 20 ons. Be sure to :00402196 65 6D 61 69 6C 20 6D 65-2E 00 57 65 6C 6C 20 64 email me..Well d Cette fois ci le serial est complet, en y ayant ajouté :-) :004012BA PUSH EAX > pousse le serial obtenu :004012BB LEA EDX,[EBP-3C] > pointe sur serial entré :004012BE PUSH EDX > poussé sur la pile :004012BF CALL KERNEL32!lstrcmp > comparaison :004012C4 TEST EAX,EAX > si EAX = 0 :004012C6 JNZ 004012E1 > -> Bad Boy :004012C8 PUSH 40 :004012CA LEA ECX,[ESI+0000012C] > titre :004012D0 PUSH ECX :004012D1 LEA EAX,[ESI+00000106] > message :004012D7 PUSH EAX :004012D8 PUSH 00 :004012DA CALL USER32!MessageBoxA > Glop! Glop! :004012DF JMP 004012FB > sortie :004012E1 PUSH 00001000 :004012E6 LEA EDX,[ESI+0000018D] > titre :004012EC PUSH EDX :004012ED LEA ECX,[ESI+00000136] > message :004012F3 PUSH ECX :004012F4 PUSH 00 :004012F6 CALL USER32!MessageBoxA > Pas Glop! Pas Glop! :004012FB POP ESI :004012FC POP EBX > restore les registres :004012FD MOV ESP,EBP :004012FF POP EBP :00401300 RET Voilà, nous sommes arrivés au bout de nos peines. lea eax, [offset Name] ; calcul de la longeur du Name push eax call lstrlen mov ebx,eax ; sauvegarde résultat lea eax, [offset Company] ; calcul de la longueur de Company push eax call lstrlen imul ebx,eax ; multiplication des 2 résultats obtenus add ebx,000F3EA9h ; ajoute nombre magique mov eax, ebx ; résultat dans eax mov edi, 0Ah ; edi est le diviseur mov ecx,05 ; initialise serial de 6 caractères boucle1: xor edx,edx ; réinitialise le registre du reste div edi ; division de eax/0A -> reste dans EDX add edx,30h ; transforme EDX (hexa) en ASCII mov [ecx+Serial],dl ; stock dans mémoire (à l'envers) dec ecx ; d'ou position -1 test eax,eax ; EAX n'est pas encore à 00 ? jne boucle1 ; recommence lea ecx, [offset cle] ; pointe sur string ":-)" push ecx lea eax, [offset Serial] ; pointe sur serial push eax call lstrcat ; concatène les deux strings |
Bonne Journée