Tuto 19 - Le ScrewMe #1 de Dynasty
Transcription
Tuto 19 - Le ScrewMe #1 de Dynasty
Tutorial No 19 Le ScrewMe #1 by Dynasty : Analyse et Keygenning Bonjour à tous, et bienvenue dans ce 19ième tutorial! Nous allons voir comment démonter mon défi le ScrewMe #1, codé en C pour www.deezdynasty.xdir.org/ Avant-propos : Le but est d'étudier plus en profondeur le traçage du code, ainsi que les protections anti-debugger les plus classiques (et les plus simples...). On en verra de plus élaborées dans le tutoriel sur l'EVAL4 d'Ezéqui3l. Pour ça, on va donc utiliser un OllyDBG 'pur' c'est a dire sans aucun plugins, pour comprendre comment se passe la détection des debuggers. La cible n'est pas difficile, mais commençons tout d'abord par l'analyser plus en détails... comprendre la routine nous permettra de pouvoir keygenner ce défi dans un second temps. Les outils nécessaires : • • • • • OllyDBG 1.10 [sans plugins] PEiD 0.94 CrackersTool [conversions] Calculator 1.5 [calculs] Le KeygenMe, trouvable ici Tous sont trouvables sur http://deezdynasty.xdir.org/downloads.html Partie 1 – Analyse / Comment attaquer la cible On l'ouvre dans Olly, et on voit bien sûr les 2 champs Name et Serial. A moins d'avoir une chance de cocu, une messagebox vous indique que vous n'avez pas le bon serial pour enregistrer le crackme à votre nom. PEiD nous indique qu'il n'est pas compressé, et codé en C. Il ne trouve pas de signature de crypto, donc pas de souci majeur. Protections : ANTI-debug * IsDebuggerPresent .text:00401716 et .text:0040177F -> NULL to bypass * FindWindowA (OllyDBG) .text:004012AC -> NULL to bypass * FindWindowA (SND) .text:0040151A -> NULL to bypass Crypto (Chiffre de César, décalage 4) * ]sy$Wgvi{ih$qi$222$Xlerow$jsv$vikmwxivmrk$xlmw$gvegoqi$%$ “You screwed me ... Thanks for registering this crackme !” * Mrzepmh$Wivmep$$ “Invalid Serial” (Chiffre de César, décalage 2) * Ejgem"vjg"Ugtkcn “Check the Serial” * Ejgem"vjg"Pcog"Hkgnf “Check the Name Field” KeyFile Serial * “EarnUrRight.dat” + "Allow Me to Retort!" * Deux parties + tiret séparateur. * GetVolumeInformationA On va commencer par ouvrir notre ScrewMe #1 sous OllyDBG, et le lancer en rentrant n’importe quelles infos avec F9. La, vu qu’on n’a aucun plugins, il ne se passe rien lorsqu’on clique sur Register. On est donc repérés… Vous pouvez tester les strings mais vous ne trouverez rien de très équivoque. Vous ne verrez que la série de signes que j’ai mis dans la section Crypto dans les protections ci-dessus. Rapidement, j’ai utilisé la méthode du chiffre de césar pour qu’elles soient méconnaissables a l’œil nu (c’est un peu bidon comme cryptage, puisqu’il ne s’agit que d’un décalage de l’alphabet d’un certain nombre de caractères et elles sont toujours apparentes). Ouvrez la liste des API sous Olly en faisant un CTRL+N. On aperçoit deux API qui sont responsable de la détection éventuelle d’un debugger. La première est très évidente, il s’agit de IsDebuggerPresent (nom très explicite !). La deuxième est, pour un œil non averti, plus sourde : il s’agit de FindWindowA. Commençons par voir IsDebuggerPresent et ce que nous dit la MSDN : The IsDebuggerPresent function indicates whether the calling process is running under the context of a debugger. This function is exported from KERNEL32.DLL. BOOL IsDebuggerPresent(VOID) Parameters This function has no parameters. Return Value If the current process is running in the context of a debugger, the return value is nonzero. If the current process is not running in the context of a debugger, the return value is zero. Remarks This function allows an application to determine whether or not it is being debugged, so that it can modify its behavior. For example, an application could provide additional information using the OutputDebugString function if it is being debugged. Aujourd’hui, on ne se soucie plus de cette fonction car elle est extrêmement simple à contourner – et les plugins contre celle-ci sont nombreux. Elle retourne simplement une valeur différente de zéro si elle tourne dans le contexte d’un debugger – ce qui est notre cas. On va donc soit se débrouiller pour lui assigner une valeur de retour positive soit jouer la facilité (par la j’entends « les bourrins ») et NOPper le CALL à cette API. Je vous laisse vous en charger, vous savez NOPper une instruction seul maintenant ! Attention, il y a deux ou trois appels à IsDebuggerPresent, juste pour nous embêter, donc faites attention de ne pas en oublier un… Si on relance notre crackme dans Olly, et qu’on reteste, il ne se passe toujours rien … il y a donc une protection supplémentaire que l’on va voir tout de suite. Passons à FindWindowA. Je ne sais pas si cette API a été conçue contre les debuggers à la base, mais on peut l’utiliser dans ce but (elle l’est beaucoup). Voici ce que nous dit la MSDN : The FindWindow function retrieves the handle to the top-level window whose class name and window name match the specified strings. This function does not search child windows. HWND FindWindow( LPCTSTR lpClassName, // pointer to class name LPCTSTR lpWindowName // pointer to window name ); Parameters lpClassName Points to a null-terminated string that specifies the class name or is an atom that identifies the class-name string. If this parameter is an atom, it must be a global atom created by a previous call to the GlobalAddAtom function. The atom, a 16-bit value, must be placed in the low-order word of lpClassName; the high-order word must be zero. lpWindowName Points to a null-terminated string that specifies the window name (the window's title). If this parameter is NULL, all window names match. Return Values If the function succeeds, the return value is the handle to the window that has the specified class name and window name. If the function fails, the return value is NULL. Pour les Anglophobes: pour faire simple, cette fonction cherche le nom d’une fenêtre que le programmeur choisit. Dans mon cas, j’ai choisi de chercher le handle d’une fenêtre appelée « OllyDBG » (que vous pouvez voir dans les strings car j’ai malencontreusement oublié de la crypter !). On met un BP avec F2 sur toutes les occurrences de cette API, et on relance le crackme. La, on breake en 004012AC. Un peu de patching s’impose pour bypasser cette protection…. On n’a qu’à suivre l’ASCII « OllyDBG » dans la fenêtre de Dump, taper CTRL+E pour éditer, et changer le nom de la classe (par exemple « DllyDBG ») … de cette manière on ne sera plus repéré. Partie 2 – Comprendre la Routine Bon, on passe maintenant à l’analyse de la routine. Pour voir où ça se passe, on va rouvrir la liste des API puisqu’on n’a pas de string de texte exploitable. On va mettre un BP sur GetDlgItemTextA, c’est celle qui récupère du texte d’une EditBox, donc tentons notre chance. On breake en 4016F3. On tombe plus ou moins la ou tout se passe. Une série de F8 pour tracer jusqu'à cette routine : ;-----------------------------------; début de la routine de création 0040178D 00401791 00401797 0040179A 0040179D 004017A2 004017A5 004017AB 004017AF 004017B5 004017B8 004017BB 004017C0 004017C3 004017C9 |> \807D D8 00 CMP BYTE PTR SS:[EBP-28],0 // regarde si un nom est rentré |. 0F84 01030000 JE 00401A98 // saute si pas de nom rentré |. 8D45 D8 LEA EAX,DWORD PTR SS:[EBP-28] // met le nom dans EAX |. 890424 MOV DWORD PTR SS:[ESP],EAX |. E8 AE0A0000 CALL <JMP.&msvcrt.strlen> \strlen |. 83F8 0B CMP EAX,0B // Cmp le nombre de lettres à 11 |. 0F87 ED020000 JA 00401A98 // saute si plus grand que 11 |. 807D E8 00 CMP BYTE PTR SS:[EBP-18],0 // regarde si un serial est rentré |. 0F84 CD020000 JE 00401A82 // saute si pas de serial |. 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18] |. 890424 MOV DWORD PTR SS:[ESP],EAX // met le serial dans EAX |. E8 900A0000 CALL <JMP.&msvcrt.strlen> \strlen |. 83F8 10 CMP EAX,10 // cmp nombre de char du serial a 16 |. 0F87 B9020000 JA 00401A82 // saute si plus de 16 char dans serial |. C785 38FFFFFF>MOV DWORD PTR SS:[EBP-C8],0 ;-------------------------------------; début de la routine 004017D3 004017D6 004017D9 004017DE 004017E4 004017E6 004017E9 004017EF 004017F2 004017F5 004017F8 004017FA 00401800 00401802 |> 8D45 D8 /LEA EAX,DWORD PTR SS:[EBP-28] |. 890424 |MOV DWORD PTR SS:[ESP],EAX |. E8 720A0000 |CALL <JMP.&msvcrt.strlen> \strlen |. 3985 38FFFFFF |CMP DWORD PTR SS:[EBP-C8],EAX // nombre de lettres passées en revue |. 73 1E |JNB SHORT 00401804 // sort de la routine a la fin du nom |. 8D45 F8 |LEA EAX,DWORD PTR SS:[EBP-8] |. 0385 38FFFFFF |ADD EAX,DWORD PTR SS:[EBP-C8] |. 83E8 20 |SUB EAX,20 |. 0FBE10 |MOVSX EDX,BYTE PTR DS:[EAX] |. 8D45 D4 |LEA EAX,DWORD PTR SS:[EBP-2C] |. 0110 |ADD DWORD PTR DS:[EAX],EDX // ajoute la valeur ASCII lettre par lettre |. 8D85 38FFFFFF |LEA EAX,DWORD PTR SS:[EBP-C8] |. FF00 |INC DWORD PTR DS:[EAX] |.^ EB CF \JMP SHORT 004017D3 // on loope pour revenir au début de la routine ; ---------------- ; Le résultat de cette routine se retrouve dans EDX juste en dessous 00401804 |> \8B55 D4 MOV EDX,DWORD PTR SS:[EBP-2C] // met le résultat dans EDX 00401807 |. 89D0 MOV EAX,EDX // … puis dans EAX 00401809 |. C1E0 02 SHL EAX,2 // cette ligne et les 2 qui suivent… 0040180C |. 01D0 ADD EAX,EDX // …multiplient le résultat précédent… 0040180E |. 01C0 ADD EAX,EAX // par 10. ex : pour “Dynasty” 2EC 1D38 00401810 |. 8945 D4 MOV DWORD PTR SS:[EBP-2C],EAX 00401813 |. C645 AC 00 MOV BYTE PTR SS:[EBP-54],0 00401817 |. 8B45 D4 MOV EAX,DWORD PTR SS:[EBP-2C] 0040181A |. 894424 08 MOV DWORD PTR SS:[ESP+8],EAX 0040181E |. C74424 04 2D4>MOV DWORD PTR SS:[ESP+4],0040412D |ASCII "%d" // indique la forme de cette partie du serial 00401826 |. 8D45 A8 LEA EAX,DWORD PTR SS:[EBP-58] 00401829 |. 890424 MOV DWORD PTR SS:[ESP],EAX 0040182C |. E8 0F0A0000 CALL <JMP.&msvcrt.sprintf> \sprintf // convertit le résultat (1D38 pour “Dynasty”) en décimal 7480 Je pense que vous devez avoir compris ce qui se passe jusque-là. On continue l’étude de la routine… 00401831 |. A1 40504000 MOV EAX,DWORD PTR DS:[405040] // on met le contenu de l’adresse 405040 dans EAX (On s’arrêtera une seconde pour regarder ça… ) 00401836 |. 894424 08 MOV DWORD PTR SS:[ESP+8],EAX 0040183A |. C74424 04 2D4>MOV DWORD PTR SS:[ESP+4],0040412D |ASCII "%d" 00401842 |. 8D45 98 LEA EAX,DWORD PTR SS:[EBP-68] 00401845 |. 890424 MOV DWORD PTR SS:[ESP],EAX 00401848 |. E8 F3090000 CALL <JMP.&msvcrt.sprintf> \sprintf // convertit ce contenu en décimal Bon on s’arrête deux secondes pour regarder ça de plus près… D’où sort ce truc que l’on met dans EAX ?! Et oui, dans la liste des APIs, on a GetVolumeInformationA … regardons ce que nous dit la MSDN : GetVolumeInformation Function Retrieves information about the file system and volume associated with the specified root directory. To specify a handle when retrieving this information, use the GetVolumeInformationByHandleW function. To retrieve the current compression state of a file or directory, use FSCTL_GET_COMPRESSION. Syntax BOOL WINAPI GetVolumeInformation( __in_opt LPCTSTR lpRootPathName, __out LPTSTR lpVolumeNameBuffer, __in DWORD nVolumeNameSize, __out_opt LPDWORD lpVolumeSerialNumber, __out_opt LPDWORD lpMaximumComponentLength, __out_opt LPDWORD lpFileSystemFlags, __out LPTSTR lpFileSystemNameBuffer, __in DWORD nFileSystemNameSize ); Parameters lpRootPathName A pointer to a string that contains the root directory of the volume to be described. If this parameter is NULL, the root of the current directory is used. A trailing backslash is required. For example, you specify \\ MyServer\MyShare as \\MyServer\MyShare\, or the C drive as "C:\". lpVolumeNameBuffer A pointer to a buffer that receives the name of a specified volume. The maximum buffer size is MAX_PATH+1. nVolumeNameSize The length of a volume name buffer, in TCHARs. The maximum buffer size is MAX_PATH+1. This parameter is ignored if the volume name buffer is not supplied. lpVolumeSerialNumber A pointer to a variable that receives the volume serial number. This parameter can be NULL if the serial number is not required. This function returns the volume serial number that the operating system assigns when a hard disk is formatted. To programmatically obtain the hard disk's serial number that the manufacturer assigns, use the Windows Management Instrumentation (WMI) Win32_PhysicalMedia property SerialNumber. lpMaximumComponentLength A pointer to a variable that receives the maximum length, in TCHARs, of a file name component that a specified file system supports. A file name component is the portion of a file name between backslashes. The value that is stored in the variable that *lpMaximumComponentLength points to is used to indicate that a specified file system supports long names. For example, for a FAT file system that supports long names, the function stores the value 255, rather than the previous 8.3 indicator. Long names can also be supported on systems that use the NTFS file system. lpFileSystemFlags A pointer to a variable that receives flags associated with the specified file system. This parameter can be one or more of the following flags. However, FS_FILE_COMPRESSION and FS_VOL_IS_COMPRESSED are mutually exclusive. En gros, elle récupère le No de série de votre volume C:\ à l’ouverture du crackme, et il est stocké a l’adresse 405040. Allons poser un BP dessus, puis on relance le crackme dans OllyDBG. Paf ! il breake en 401B09. Là on peut observer les paramètres suivants dans la pile : 0022FED0 004041A1 |RootPathName = "C:\" 0022FED4 00405024 |VolumeNameBuffer = ScrewMe_.00405024 0022FED8 0000000C |MaxVolumeNameSize = C (12.) 0022FEDC 00405040 |pVolumeSerialNumber = ScrewMe_.00405040 0022FEE0 00000000 |pMaxFilenameLength = NULL 0022FEE4 00000000 |pFileSystemFlags = NULL 0022FEE8 00405030 |pFileSystemNameBuffer = ScrewMe_.00405030 0022FEEC 00000010 \pFileSystemNameSize = 00000010 0022FEF0 7C812F28 kernel32.7C812F28 Cette adresse 405040 est rappelée en 401831 pour être remise dans EAX, et pouvoir faire des calculs dessus. C’est aussi simple que ça ! Continuons l’étude de la routine… 0040184D |. C645 EC 2D MOV BYTE PTR SS:[EBP-14],2D // met un “-“ (2Dh) dans le stack 00401851 |. 8D45 D0 LEA EAX,DWORD PTR SS:[EBP-30] 00401854 |. FF00 INC DWORD PTR DS:[EAX] 00401856 |. 8B85 40FFFFFF MOV EAX,DWORD PTR SS:[EBP-C0] 0040185C |. 0385 44FFFFFF ADD EAX,DWORD PTR SS:[EBP-BC] 00401862 |. 0385 3CFFFFFF ADD EAX,DWORD PTR SS:[EBP-C4] 00401868 |. 05 C0070000 ADD EAX,7C0 // met “1984” dans EAX 0040186D |. 0FAF45 D4 IMUL EAX,DWORD PTR SS:[EBP-2C] // multiplie 1984 par la première partie de notre serial (total des valeurs ASCII de notre nom) 00401871 |. 8945 94 MOV DWORD PTR SS:[EBP-6C],EAX 00401874 |. A1 40504000 MOV EAX,DWORD PTR DS:[405040] 00401879 |. 3345 94 XOR EAX,DWORD PTR SS:[EBP-6C] // (1984*total valeurs ASCII) XOR (ID du disque dur en décimal) 0040187C |. 8945 90 MOV DWORD PTR SS:[EBP-70],EAX 0040187F |. 8B45 90 MOV EAX,DWORD PTR SS:[EBP-70] 00401882 |. 894424 08 MOV DWORD PTR SS:[ESP+8],EAX 00401886 |. C74424 04 2D4>MOV DWORD PTR SS:[ESP+4],0040412D |ASCII "%d" 0040188E |. 8D85 78FFFFFF LEA EAX,DWORD PTR SS:[EBP-88] 00401894 |. 890424 MOV DWORD PTR SS:[ESP],EAX 00401897 |. E8 A4090000 CALL <JMP.&msvcrt.sprintf> \sprintf // convertit le résultat de tout ça en décimal (visible dans la pile) ;--------------------------------------------------------------------- ; Fin de la routine de création On passe ensuite à la routine de comparaison de tout ça… tracez vous-mêmes et vous verrez qu’il compare caractère par caractère, pour déterminer si on saute au goodboy ou a badboy ;) Ceci n'a rien de compliqué, je vous laisse l'étudier seul: ; ------------------------------------------------------------- ; Début de la routine de comparaison 0040189C 004018A6 004018AC 004018AF 004018B4 004018BA 004018BC 004018BF 004018C5 004018C8 004018CB 004018D1 004018D4 004018D7 004018D9 004018DB 004018DE 004018E0 004018E6 004018E8 004018EA 004018F4 004018FB 004018FD 00401900 00401906 00401909 0040190C 00401912 00401915 00401918 0040191A 0040191C 0040191F 00401921 00401927 00401929 0040192B 0040192E 00401931 |. C785 38FFFFFF>MOV DWORD PTR SS:[EBP-C8],0 |> 8D85 78FFFFFF /LEA EAX,DWORD PTR SS:[EBP-88] |. 890424 |MOV DWORD PTR SS:[ESP],EAX |. E8 9C090000 |CALL <JMP.&msvcrt.strlen> \strlen |. 3985 38FFFFFF |CMP DWORD PTR SS:[EBP-C8],EAX |. 73 2E |JNB SHORT 004018EA |. 8D45 F8 |LEA EAX,DWORD PTR SS:[EBP-8] |. 0385 38FFFFFF |ADD EAX,DWORD PTR SS:[EBP-C8] |. 8D48 80 |LEA ECX,DWORD PTR DS:[EAX-80] |. 8D45 F8 |LEA EAX,DWORD PTR SS:[EBP-8] |. 0385 38FFFFFF |ADD EAX,DWORD PTR SS:[EBP-C8] |. 8D50 F5 |LEA EDX,DWORD PTR DS:[EAX-B] |. 0FB601 |MOVZX EAX,BYTE PTR DS:[ECX] |. 3A02 |CMP AL,BYTE PTR DS:[EDX] |. 75 05 |JNZ SHORT 004018E0 |. 8D45 D0 |LEA EAX,DWORD PTR SS:[EBP-30] |. FF00 |INC DWORD PTR DS:[EAX] |> 8D85 38FFFFFF |LEA EAX,DWORD PTR SS:[EBP-C8] |. FF00 |INC DWORD PTR DS:[EAX] |.^ EB BC \JMP SHORT 004018A6 |> C785 38FFFFFF>MOV DWORD PTR SS:[EBP-C8],0 |> 83BD 38FFFFFF>/CMP DWORD PTR SS:[EBP-C8],3 |. 7F 2E |JG SHORT 0040192B |. 8D45 F8 |LEA EAX,DWORD PTR SS:[EBP-8] |. 0385 38FFFFFF |ADD EAX,DWORD PTR SS:[EBP-C8] |. 8D48 F0 |LEA ECX,DWORD PTR DS:[EAX-10] |. 8D45 F8 |LEA EAX,DWORD PTR SS:[EBP-8] |. 0385 38FFFFFF |ADD EAX,DWORD PTR SS:[EBP-C8] |. 8D50 B0 |LEA EDX,DWORD PTR DS:[EAX-50] |. 0FB601 |MOVZX EAX,BYTE PTR DS:[ECX] |. 3A02 |CMP AL,BYTE PTR DS:[EDX] |. 75 05 |JNZ SHORT 00401921 |. 8D45 D0 |LEA EAX,DWORD PTR SS:[EBP-30] |. FF00 |INC DWORD PTR DS:[EAX] |> 8D85 38FFFFFF |LEA EAX,DWORD PTR SS:[EBP-C8] |. FF00 |INC DWORD PTR DS:[EAX] |.^ EB C9 \JMP SHORT 004018F4 |> 8D45 A8 LEA EAX,DWORD PTR SS:[EBP-58] |. 890424 MOV DWORD PTR SS:[ESP],EAX |. E8 1A090000 CALL <JMP.&msvcrt.strlen> \strlen ------------------------------------------------------------- ; Fin routine cmp [...] On n’en a pas fini avec notre crackme. Même après avoir fishé le serial dans la partie de comparaison en regardant a quoi il compare notre serial bidon, le ScrewMe nous affiche toujours cette horrible MsgBox : En effet, il y a encore une protection supplémentaire : la recherche d’un keyfile. Celui-ci doit s’appeler « EarnUrRight.dat » et contenir « Allow Me to Retort » pour pouvoir être valide, et doit être dans le même dossier que le ScrewMe. Les API utilisées sont CreateFileA et ReadFile (un bp sur ces APIs vous suffira pour suivre ce qui se passe), mais je ne vais pas m’étaler sur ce sujet. Pour cela, je vous renvoie directement sur le tutorial #18 de mon site, dans lequel le concept du keyfile est étudié. Partie 3 – Faire un KeyGen en ASM On va maintenant voir comment on peut faire un keygen pour notre crackme en ASM, qui pourra nous donner un serial valide pour le nom que l’on va entrer, et en même temps nous créer le keyfile valide dans le même dossier. Il s’agit la d’être attentif… si vous n’avez jamais fait de programmation ou que vous n’avez jamais touché a l’assembleur, vous verrez au moins a quoi ressemble le code. Pour cela, j’ai utilisé WinAsm Studio 5.1.5.0 qui est gratuit, et le compilateur MASM32, gratuit également. Avant de se lancer, on va devoir s’assurer d’avoir bien compris la routine et ce que fait le crackme pour avoir un serial valide… c’est une étape primordiale. N’hésitez pas a relancer OllyDBG et à observer en détails ce qui se passe en traçant. Pour « Dynasty », on trouve un résultat de la forme « 7480-xxxxxxxxxx ». Bien entendu, impossible de vous donner la deuxième partie puisqu’elle dépend du No de série du disque dur, elle sera donc différente pour tous les PC. La première partie est constante: on ne calcule que la totalité des valeurs ASCII des lettres, qui est donc identique quelque soit l’ordre des lettres (essayez avec par exemple « ystDnya»). En revanche elle influe sur la deuxième partie du serial, qui elle dépend du PC que vous utilisez ET de votre nom. Récapitulation du calcul du serial 1ere partie : • • • • Récupération du No de série de notre disque dur a l’ouverture, mis en 405040 Ajout des valeurs ASCII de chaque lettre de notre nom Multiplication du total des valeurs ASCII par 10 Concaténation de 2Dh derrière ce résultat, soit un tiret en ASCII ---------------------------------------------------------------------------------------------------2eme partie : • • • • • Récupération du No de Volume C:\\ en 405040 Multiplication de la première partie du serial par 1984 (1ère partie serial*1984) XOR (No série du volume C:\) pour obtenir la 2ème partie du serial Concaténation des 2 parties, avec le tiret au milieu Si on entre un serial valide, recherche d’un Keyfile « EarnUrRight.dat » + « Allow Me to Retort ! » Voyons aussi le logigramme qu'a fait kirjo pour l'étude du CrackMe, avec un coté plus visuel: Voici enfin la source de mon Keygen commentée : Keygen.asm ;=============================================== ; **************************** | ; | ; Source Keygen by Dynasty | ; March, 2008 | ; | ; ScrewMe #1 | ; | ;=============================================== ;=========================== ; INCLUDES .386 .model flat,stdcall option casemap:none include include include includelib includelib includelib DlgProc windows.inc kernel32.inc user32.inc kernel32.lib user32.lib winmm.lib proto :DWORD,:DWORD,:DWORD,:DWORD ;=========================== ; SECTION .DATA .data ; contenu des msgbox et déclaration des variables SysFile dd 0 NumSerie dd 0 NomVolD dd 0 DiskC db "C:\\",0 Formatd db "%d",0 KeyFile db "EarnUrRight.dat",0 hFile HANDLE ? Chaine BYTE "Allow Me to Retort!",0 BufSize=( $ - Chaine ) Nbr_Octet_Ecrit DWORD ? MsgBoxCaption MsgBoxText db db db db db db "About this Keygen...",0 ; barre de titre de la msgbox "Coded by Dynasty",13,10 ; contenu de la msgbox " ",13,10 ; 13,10 indique plusieurs lignes ds la msgbox "Come and play at",13,10 "http://deezdynasty.xdir.org/forum/",13,10 " and find more challenges there :)",0 ; 0 indique fin msgbox MsgBoxCaptionError db "Error",0 MsgBoxTextError db "Keyfile 'EarnUrRight.dat', could not be created",0 MsgBoxCaptionOK MsgBoxTextOK db "Success!",0 db "The Keyfile 'EarnUrRight.dat' was created successfully!",0 MsgBoxAskCaption MsgBoxAskText db "Question:",0 db "Do you want to create keyfile 'EarnUrRight.dat'?",0 ;=========================== ; SECTION .DATA? .data? hInstance HINSTANCE NameBuffer db SerialBuffer db ? 32 dup(?) 32 dup(?) .const IDD_KEYGEN IDC_NAME IDC_SERIAL IDC_GENERATE 1001 1002 1003 1004 equ equ equ equ ; DECLARATION ID DES BOUTONS IDC_COPY IDC_EXIT IDM_ABOUT IDM_EXIT IDM_OTHERS DYNcon equ equ equ equ equ equ 1005 1006 1101 1102 1104 2001 ;=========================== ; SECTION .CODE .code start: invoke mov invoke invoke GetModuleHandle, NULL hInstance,eax DialogBoxParam, hInstance, IDD_KEYGEN, NULL, addr DlgProc, NULL ExitProcess,eax ;=========================== ; DIALOG PROCESS, ACTION DES BOUTONS DlgProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM .if uMsg == WM_INITDIALOG invoke LoadIcon,hInstance,DYNcon invoke SendMessage,hWnd,WM_SETICON,1,eax invoke GetDlgItem,hWnd,IDC_NAME invoke SetFocus,eax .elseif uMsg == WM_COMMAND mov eax,wParam .if eax==IDC_GENERATE invoke GetDlgItemText,hWnd,IDC_NAME,addr NameBuffer,32 call Generate invoke SetDlgItemText,hWnd,IDC_SERIAL,addr SerialBuffer .elseif eax==IDM_ABOUT invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK .elseif eax==IDM_EXIT invoke SendMessage, hWnd, WM_CLOSE, 0, 0 .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 .elseif uMsg == WM_COMMAND mov eax,wParam .endif .elseif uMsg == WM_CLOSE invoke EndDialog,hWnd,0 .endif xor eax,eax ret DlgProc endp ;=========================== ; DEBUT ROUTINE DE GENERATION SERIAL Generate proc invoke lstrlen, addr NameBuffer ; on regarde si un nom est entré dans « name » test eax, eax jle NOINPUT ; si pas de nom entré, on saute vers “pas d’action” en bas (RETN) mov esi, offset NameBuffer ; on met le nom dans ESI mov edi, offset SerialBuffer ; on met le serial dans EDI xor ebx,ebx ; on remet EBX et EDX à zéro pour travailler avec xor edx,edx @@: push eax xor eax,eax ; on met EAX à zéro mov al, byte ptr [esi+edx] add ebx,eax ; ajoute les valeurs hexa du nom a EBX a chaque loop inc edx ; on incrémente pour passer a la lettre suivante pop eax cmp eax,edx ; on regarde le nombre de char passés en revue jne @b ; saute vers le @@: , on loope jusqu'à la fin du nom mov eax,10 mul ebx ; multiplie EBX par 10 push eax invoke wsprintf,offset SerialBuffer,offset Formatd,eax ; convertit en décimal et ajoute la forme « %d » mov eax,45 ; on met le tiret dans EAX mov esi, offset add esi,4 ; mov [esi],eax pop eax mov ebx,1984 ; mul ebx ; push eax SerialBuffer on ne garde que les 4 1ers char on met 1984 dans EBX on le multiplie par total valeurs ASCII du nom @@: push 16 ; paramètres de GetVolumeInformation push SysFile push 0 push 00 push offset NumSerie push 12 push NomVolD push offset DiskC call GetVolumeInformation ; récupère les infos sur notre DD pop eax mov ebx, NumSerie xor eax,ebx invoke wsprintf, offset SerialBuffer+5, offset Formatd, eax ; ajoute la 2eme partie du serial a la 1ere, a partir du 5eme char (xxxx-xxxxxxxxxx) @@: ; création du keyfile invoke MessageBox,NULL,addr MsgBoxAskText,addr MsgBoxAskCaption,MB_YESNO .if eax ==7 ; si on clique sur « non »… jmp @f ; on saute la création du keyfile .endif invoke CreateFile,ADDR KeyFile, GENERIC_READ or GENERIC_WRITE , FILE_SHARE_READ or FILE_SHARE_WRITE,\ NULL,CREATE_NEW,FILE_ATTRIBUTE_ARCHIVE,\ NULL .if eax==-1 invoke CreateFile,ADDR KeyFile, GENERIC_READ or GENERIC_WRITE , FILE_SHARE_READ or FILE_SHARE_WRITE,\ NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,\ NULL .endif .if eax==-1 invoke MessageBox,NULL,addr MsgBoxTextError,addr MsgBoxCaptionError, MB_OK ; invoque message d’erreur si impossible de créer le keyfile .else mov hFile,eax INVOKE WriteFile, hFile, ADDR Chaine, BufSize , ADDR Nbr_Octet_Ecrit, 0 invoke CloseHandle,hFile invoke MessageBox,NULL,addr MsgBoxTextOK, addr MsgBoxCaptionOK,MB_OK ; invoque message de réussite si creation du keyfile OK .endif @@: NOINPUT: ; au cas où on n’aie pas rentré de nom ret Generate endp ; on indique la fin de la routine end start Il est facile de trouver un template tout fait sur internet, donc vous pourrez mettre en application de code vous-mêmes. Smell ya later ;) Remerciements : ● ● ● Tout particuliers a Ezéqui3l, il sait pourquoi. Tous les membres du Forum DeezDynasty ;) uLysse_31 parce-que j'ai envie et qu'il assure :) ● Baboon, Kaine, kaze, tretsah, et ceux que j'oublie et qui m'ont apporté leur aide pour le keygen comme pour le reste. ● Remerciements également a Silkut pour m'avoir fourni ses notes d'étude du KeygenMe, et que j'ai réutilisées dans la partie « Protection ». ● Kirjo pour son logigramme du KeygenMe. Tutorial by Dynasty – http://deezdynasty.xdir.org/