Je m’attaque dans cet article à un CrackMe de niveau fastidieux « for newbies ».
Pour download le KeygenMe : https://www.re-xe.com/wp-content/uploads/2010/11/The_Analyst_k4n1.zip
On ne perd pas de temps, on y va, on lance le binaire.
On obtient la chose suivante :
On analyse ensuite le binaire à coup de PEiD, ça m’étonnerait qu’on l’ai packé, étant donné qu’il est d’un niveau relativement facile.
On obtient la chose suivante :
Parfait, pas de packer en vue. Mais peut-être qu’on aura le droit à de la jolie crypto ? On utilise le plugin Kanal de PEiD, voici ce que l’on obtient :
Super, pas de crypto en vue ! On peut maintenant passer à l’analyse du binaire !
On tente un name & un serial au hasard :
W00t, This serial is *NOT* valid!
Allez op ! On ouvre notre binaire depuis Ollydbg.
Une fois votre binaire ouvert, tentez de checker les BadBoy & GoodBoy. Pour ce faire, nous allons lister les strings.
Cliquez droit dans ollydbg, puis allez dans ‘Ultra String Reference’ –> ‘1 Find Ascii’. On obtient ceci :
On aperçoit notre GoodBoy, et notre BadBoy :
Goob Boy : ‘Congratulation! IF this number comes *FROM YOUR* keygen …’
Bad Boy : ‘This serial is *NOT* Valid!! Try again… : UNREGISTRED’
On aperçoit aussi : %1X, ceci sert à formater notre string entrée.
Double cliquez sur le GoodBoy, ce qui vous renverra à son adresse (00401157) dans Ollydbg.
On obtient le code ci-dessous :
1 2 3 4 5 6 7 8 9 | 00401157 |.>PUSH 0040B43C ; /Text = « Congratulations! IF this number comes *FROM YOUR* keygen, Write a tutorial dude . » 0040115C |.>PUSH ESI ; |hWnd 0040115D |.>CALL <JMP.&USER32.SetWindowTextA>; \SetWindowTextA // G00d Boy 00401162 |.>JMP SHORT 0040117C00401164 |>>PUSH 0040B490 ; /Text = « This serial is *NOT* Valid!! Try again… : UNREGISTERED » 00401169 |.>PUSH ESI ; |hWnd 0040116A |.>CALL <JMP.&USER32.SetWindowTextA>; \SetWindowTextA // Bad Boy 0040116F |.>JMP SHORT 0040117C00401171 |>>PUSH 0040B4C9 ; /Text = « Name must contain more than 4 chars and less than 50 chars !! » 00401176 |.>PUSH ESI ; |hWnd 00401177 |.>CALL <JMP.&USER32.SetWindowTextA>; \SetWindowTextA // Bad Boy |
Très bien.
On s’intéresse ensuite à cette ligne (située juste au dessus) :
1 | 004010AE |. E8 FB9B0000 CALL <JMP.&USER32.GetWindowTextA> ; \GetWindowTextA |
Ici GetWindowtext sert à récupérer les valeurs du champ name.
Ensuite, nous avons :
1 2 3 4 5 | 00401129 |. FF75 C8 PUSH DWORD PTR [EBP-38] ; /Arg3 0040112C |. 68 38B44000 PUSH 0040B438 ; |%lX 00401131 |. 8D8D 80FEFFFF LEA ECX,[EBP-180] ; | 00401137 |. 51 PUSH ECX ; |Arg1 00401138 |. E8 873D0000 CALL 00404EC4 ; \k4n.00404EC4 |
Ceci ressemble fortement à une fonction qui va formater notre string. Ce qui nous l’indique c’est le ‘ASCII « %lX »‘.
On descend encore un peu et on tombe sur :
1 | 0040114E |. E8 339C0000 CALL <JMP.&KERNEL32.lstrcmpA> ; \lstrcmpA |
lstrcmp s’occupe de comparer les chaines entrées juste avant d’afficher le BadBoy ou GoodBoy. On peut facilement en déduire que la routine de génération du serial est dans le coin ! Yes on s’en rapproche …
Le fait que’on ai une boucle juste avant, et que le résultat de celle-ci soit passé en argument à la fonction de formatage indique qu’on est tout proche de la routine !
On remonte un peu plus haut, et on trouve :
1 2 | 00401102 |. 837D D8 32 CMP DWORD PTR SS:[EBP-28],32 00401106 |. 7D 69 JGE SHORT k4n.00401171 |
Ici le programme vérifie la taille du name entré : si il est supérieur ou égal à 50d. (c’est à dire 50 chars). Si on entre un name supérieur à 50 chars, le programme nous enverra directement vers un BadBoy.
Vous pouvez en avoir le cœur net maintenant ! On est sûr et certain que la routine est située entre les vérifications de la taille de la string entrée en name et son formatage. C’est à dire de :
1 | 0040110C |> /0FBE840D 48FFFFFF /MOVSX EAX,BYTE PTR [EBP+ECX-B8] |
À :
1 | 00401124 |. 03C3 ADD EAX,EBX |
On ne prendra pas :
1 | 00401126 |. 8945 C8 MOV [EBP-38],EAX |
Car cette ligne ne fait que sauvegarder eax dans la pile. La valeur de eax ne changera donc plus.
La routine correspond donc à ceci (lignes grisées) :
On se procure cette routinz & on code notre keygen !!
Quelques commentaires à propos de la routine :
1 2 3 4 5 6 7 8 9 | MOV AL,BYTE PTR [ecx+NameBuffer] ; Met dans AL chaque char du name un par un à chaque fois que ça boucle ( car ECX est incrémenté ligne suivante ). INC ECX ; Incrémentation de ECX pour le définir comme compteur de chars contenus dans le name. XOR EAX,ECX ADD EBX,EAX ; Additionne eax à ebx. eax contenant les chars du name. CMP ECX,DWORD PTR [LenName] ; Compare le nombre de chars du compteur au nombre de chars du name. jne @B ; Boucle si le résultat du cmp précédent n'a pas été égal à 0, c'est à dire si ecx, le compteur, n'a pas été égal au nombre de chars du nom. IMUL EAX,EAX,6 ; Multiplie le char pécédemment xoré par 6. SHL EBX,7 ; Multiplie ebx par 2^7. ADD EAX,EBX ; Additionne eax à ebx. |
Lors de cette opération : ADD EAX,EBX
On a :
– eax qui est la valeur hexadécimale du dernier char multiplié par 6.
– ebx : la somme de tous les chars xorés, multipliés par 2^7.
Je vous laisse cogiter …
Keygen.asm :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | .386 .model flat,stdcall option casemap:none ; Include sur les librairies utilisées : include Keygen.inc ; Les variables déclarées : .data Format db "%X",0 Message db "Entrez moins de caractères !",0 MessageShort db "Entrez plus de caractères !",0 WinTitle db "First Keygen By int_0x80",0 ; Les variables non déclarées : .data? hInstance HINSTANCE ? NameBuffer db 50 dup(?) SerialBuffer db 50 dup(?) LenName db 50 dup(?) .code start: invoke GetModuleHandle, NULL mov hInstance, eax AllowSingleInstance addr WinTitle ; Cette macro limite le programme à une instance. invoke DialogBoxParam, hInstance, IDD_KEYGEN, NULL, addr DlgProc, NULL invoke ExitProcess, eax ; Début des procédures du dialogue : DlgProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM ; http://msdn.microsoft.com/en-us/library/ms645469(VS.85).aspx .if uMsg == WM_INITDIALOG invoke LoadIcon,hInstance,MyIcon invoke SendMessage,hWnd,WM_SETICON,1,eax invoke GetDlgItem,hWnd,IDC_NAME invoke SetFocus, eax invoke uFMOD_PlaySong, IDM_MUSIK, 0, XM_RESOURCE ; Play SFX :) .elseif uMsg == WM_COMMAND mov eax,wParam .if eax == IDC_GENERATE invoke GetDlgItemText,hWnd,IDC_NAME,addr NameBuffer,sizeof NameBuffer CALL Generate invoke SetDlgItemText,hWnd,IDC_SERIAL,addr SerialBuffer .elseif eax == IDC_COPY invoke SendDlgItemMessage,hWnd,IDC_SERIAL,EM_SETSEL,0,-1 invoke SendDlgItemMessage,hWnd,IDC_SERIAL,WM_COPY,0,0 .elseif eax == IDC_EXIT invoke SendMessage,hWnd,WM_CLOSE,0,0 .endif .elseif uMsg == WM_CLOSE invoke EndDialog,hWnd,0 .endif xor eax,eax ; MOV eax, 0 xor ecx, ecx ; MOV ecx, 0 Ret DlgProc EndP ; Fin de la procédure du dialogue. ;////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ; Génération du serial Generate proc hWnd:HWND invoke lstrlen,addr NameBuffer mov dword ptr [LenName], eax ; Stockage de eax dans LenName . cmp eax, 04h ; On compare la taille à 4d. jl NoInput ; On saute si inférieur, le je saute si c'est égal. cmp eax, 32h ; Puis on la compare à 50d (50 chars max). ja Erreur ; On saute vers le label appelé 'erreur' si supérieur. xor eax, eax ; MOV eax, 0 xor ebx, ebx ; MOV ebx, 0 xor ecx, ecx ; MOV ecx, 0 xor edx, edx ; MOV edx, 0 ; Routine @@: MOV AL,BYTE PTR [ecx+NameBuffer] ; Met dans AL chaque char du name un par un à chaque fois que ça boucle ( car ECX est incrémenté ligne suivante ). INC ECX ; Incrémentation de ECX pour le définir comme compteur de chars contenus dans le name. XOR EAX,ECX ADD EBX,EAX ; Additionne eax à ebx. eax contenant les chars du name. CMP ECX,DWORD PTR [LenName] ; Compare le nombre de chars du compteur au nombre de chars du name. jne @B ; Boucle si le résultat du cmp précédent n'a pas été égal à 0, c'est à dire si ecx, le compteur, n'a pas été égal au nombre de chars du nom. IMUL EAX,EAX,6 ; Multiplie le char pécédemment xoré par 6. SHL EBX,7 ; Multiplie ebx par 2^7. ADD EAX,EBX ; Additionne eax à ebx. ; Fin de la routine. ; Conversion du sérial : invoke wsprintf,addr SerialBuffer,addr Format,eax ; wsprintf --> http://msdn.microsoft.com/en-us/library/aa450993.aspx Ret ; Fin de la procédure de génération du serial. ; On arrive ici si il n'y a pas de nom entré : NoInput: invoke SetDlgItemText,hWnd,IDC_SERIAL, addr MessageShort Ret ; On arrive ici si le nom fait plus de 20 caractères : Erreur : invoke SetDlgItemText,hWnd,IDC_SERIAL, addr Message Ret ;////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Generate endp end start ; Fin du programme |
Keygen.inc :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | ; Includes sur les librairies utilisées : include MyMacros.asm include windows.inc include user32.inc include kernel32.inc include comctl32.inc include winmm.inc includelib kernel32.lib includelib user32.lib includelib comctl32.lib includelib winmm.lib include SFX/ufmod.inc ; Pour la musique. includelib SFX/ufmod.lib ; Aussi pour la musique. ; Prototype des fonctions : DlgProc PROTO :DWORD,:DWORD,:DWORD,:DWORD ; Définition des constantes ici : IDD_KEYGEN equ 1001 IDC_NAME equ 1002 IDC_SERIAL equ 1003 IDC_GENERATE equ 1004 IDC_COPY equ 1005 IDC_EXIT equ 1006 LOGO equ 1007 MyIcon equ 1008 IDM_MUSIK equ 1009 |
Keygen.rc :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | #define IDD_KEYGEN 1001 #define IDC_NAME 1002 #define IDC_SERIAL 1003 #define IDC_GENERATE 1004 #define IDC_COPY 1005 #define IDC_EXIT 1006 #define LOGO 1007 #define Serial 1011 #define Name 1012 #define MyIcon 1013 #define IDM_MUSIK 1009 LOGO BITMAP DISCARDABLE "int_0x800.bmp" MyIcon ICON DISCARDABLE "favicon.ico" IDM_MUSIK RCDATA DISCARDABLE "SFX/Class cracktro #15.xm" IDD_KEYGEN DIALOGEX 0,0,262,215 CAPTION "First Keygen By int_0x80" FONT 8,"MS Sans Serif" STYLE 0x10cc0000 EXSTYLE 0x00000000 BEGIN CONTROL "#1007",LOGO,"Static",0x1080020e,-7,0,277,222,0x00000000 CONTROL "Your Name",IDC_NAME,"Edit",0x50010080,53,92,191,19,0x00000200 CONTROL "",IDC_SERIAL,"Edit",0x50010080,53,120,191,19,0x00000200 CONTROL "Generate",IDC_GENERATE,"Button",0x50010000,17,166,51,16,0x00000000 CONTROL "Serial:",Serial,"Static",0x50000001,7,123,37,13,0x00000000 CONTROL "Name:",Name,"Static",0x10000001,7,95,37,13,0x00000000 CONTROL "Exit",IDC_EXIT,"Button",0x50010000,190,166,51,16,0x00000000 CONTROL "Copy",IDC_COPY,"Button",0x50010000,103,166,51,16,0x00000000 END |
MyMacros.asm :
1 2 3 4 5 6 7 8 9 10 11 12 | AllowSingleInstance MACRO lpTitle invoke FindWindow,NULL,lpTitle cmp eax, 0 je @F push eax invoke ShowWindow,eax,SW_RESTORE pop eax invoke SetForegroundWindow,eax mov eax, 0 ret @@: ENDM |
Voici à quoi ressemble le miens :
J’ai entré ‘int0x80’ en name, et j’ai cliqué sur Generate. Le serial que l’on me propose est : ‘139CA’.
Retournons vers notre KeygenMe & essayons le serial …
W00t ! KeygenMe Keygenné ! Bien joué ! Si vous galérez pour coder votre keygen, n’hésitez pas à jeter un œil au miens que voici :
(exécutable + sources) —> https://www.re-xe.com/wp-content/uploads/2010/11/KeyGen.zip
Have fun et bon courage pour la suite ! Greetz to Horgh pour m’avoir aidé à coder ce Keygen !
Int_0x80 fait ses pas en RCE. Good luck & bonne continuation boy.
<3