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/