Ecrire un KeyGenerator-Patcher et faire de Zolies box en ASM
Inintiation à l'assembleur
For Newbies Only
Official CrackMe#4
By Christal avec des commentaires de X-or
Commençons par définir les éléments qui vont permettre
de trouver comment le serial est généré.
Part I
Tripatouillage autour d'un serial:
Chaque programme est un cas particulier. Beaucoup d'entre eux peuvent se ranger dans tels ou tels catégories,
d'autres sont atypiques...
Ce crackme est particulièrement simple à cerner, que ce soit en Live Approach ou en Dead listing.
Par contre, la cible étant compressée par UPX, pour obtenir les StringsData qui vont bien, le plus
simple est d'utiliser UPX lui même pour décompresser l'application, avant d'utiliser Wdasm::00457E2A mov edx, dword ptr [ebp-04]
:00457E2D pop eax
:00457E2E cal1 00403B84
:00457E33 jne 00457E4F
:00457E35 push 00000000
* Possible StringData Ref from Code 0bj ->"Congratz !"
:00457E37 mov ecx, 00457F38
* Possible StringData Ref from Code 0bj
->"You cracked the CFF CrackMe #4 "
->"! P1ease send your solution to "
->"acidbytes@gmx.net !"
Une fois ciblé la string data "positive", l'ensemble Call/JNE va être l'aiguillage Good Boy/Bad Boy, et le générateur du code va se situer juste au dessus. Son étude manquant du moindre intéret, il n'y aurait pas là matière à une tut, si je ne voualais pas m'amuser à créer des boite de dialog un peu plus sympas que d'habitude...
.386 ; les fonctions utilisées sont compatibles ; avec l'architecture du 386 (vous avez ; aussi le 486 et le 586 pour les Pentium) .model flat, stdcall option casemap :none ; case sensitive ; il différenciera majuscule et minuscule
;=========================================================================
Les includes (inc) et les library (lib) vont êtres choisis en fonctions des
APIs que vous utiliserez.
Les fonctions de l'api Windows sont contenues dans des librairies
chargées dynamiquement au démarrage du programme. Ces librairies (les DLLs) les plus couramment utilisées
sont kernel32.dll pour la gestion des processus et de la mémoire, user32.dll pour l'interface et gdi32.dll
pour les fonctions graphiques. Le problème est que le programme ne charge pas toutes les dlls au démarrage.
Il faut donc préciser à MASM que vous utilisez une fonction externe mais en plus qu'elle doit être
importée d'une dll précise. Pour indiquer qu'une fonction externe est utilisée dans le module
(le fichier source asm) il faut ajouter au début du module les lignes qui suivent :
include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc include \masm32\include\gdi32.inc include \masm32\include\comdlg32.inc include \masm32\include\masm32.inc include \masm32\include\shell32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib includelib \masm32\lib\gdi32.lib includelib \masm32\lib\comdlg32.lib includelib \masm32\lib\masm32.lib includelib \MASM32\LIB\shell32.lib
Il y a encore un point à préciser sur les fonctions de l'api win32. Dans la documentation officielle vous trouverez le nom standard des fonctions, mais dans les dlls vous aurez des fonctions du même nom succédées d'un A ou d'un W (par ex MessageBoxA ou MessageBoxW. Le A signifie ANSI et le W signifie UNICODE, ces deux lettres précisent donc le format des chaînes de caractères utilisées par les fonctions donc celles que vous passez en paramètre et celles que vous recevez en retour de fonction.
;=========================================================================
;déclaration des macros :
szText MACRO Name, Text:VARARG -> permet d'inclure dans le code une chaine de caractères LOCAL lbl jmp lbl Name db Text,0 lbl: ENDM LoadSingle MACRO PieceOfVariable, bBrush invoke LoadImage ,hInstance, 200, IMAGE_BITMAP, 0, 0, 0 ; charge l'image de fond d'écran mov hSkin&PieceOfVariable , eax IF bBrush
invoke CreatePatternBrush, eax mov hbSkin&PieceOfVariable, eax
ENDIF ENDM
;========================================================================= ; Et pour commencer, il faut déclarer les constantes ; et les variables dont on aura besoin : .const ;========================================================================= IDC_Edit1 equ 103 ; Le premier champ d'édition (Name) se voit affecter la valeur 103 IDC_Edit2 equ 104 ; Dans la mesure ou le fichier RSRC, dans lequel se IDC_Edit3 equ 105 ; trouve les informations de traçage de la boite IDC_Exit equ 109 ; de dialogue, vous n'êtes pas libre de choisir ici l'ID IDC_Gen equ 110 IDC_Copy equ 111 IDC_Static equ 101 IDC_Url equ 115 IDC_About equ 112 IDC_Brownse equ 113 IDC_Patch equ 114 ID_BACKUP equ 116 ICON_SMALL equ 0 IDI_ICON equ 500 ;========================================================================= .data ; Données dont vous aurez besoin ;=========================================================================
; chacunes de ces strings vont être stockées à une adresse précise,
lors de
; la compilation. Elles doivent impérativement se terminer par un 0
; (NULL Terminated String)
hIconImage dd 0 -> réservation d'un espace mémoire hIcon dd 0 -> pour le stockge des handles hEdit1 dd 0 -> qui seront nécessaires. hEdit2 dd 0 hBACKUP dd 0 hStatImage dd 0 hBmp dd 0 dlgname db "dialogbox",0 StaticClass db "static",0 error db "Name supérieur à 6 digits, please...",0 url db "www.christal.citeweb.net",0 url_text db "http://Christal.ctw.net",0 open_site db "open",0 TextAbout db " Official CrackMe #4",0Dh,0Ah,0Ah db " - KeyGenarator",0Dh db " - Memory Patch ",0Dh,0Ah,0Ah db " Coded by Christal ",0
; TextAbout s'affiche sur quatre lignes, dans la boite About, avec un CR (0D, 0A)
caption_msg db "Official CrackMe#4",0 running db "Process en cours:",0Dh,0Ah db "Veuillez fermer l'application",0 bad_version db "Mauvaise version...",0 success db "Cible patchée... ",0 cible_abs db "Cible Absente...",0 lg_first DWORD 0 -> mémoires réservées lg_last DWORD 0 -> initialisées à 0 Lg_path DWORD 0 FilterString db "CrackMe#4",0,"CrackMe#4.exe",0 db "Tous les Fichiers",0,"*.*",0,0
cible_ok db "Fichier chargée...",0 szBackupName db MAX_PATH dup(0) szBackupExt db ".bak",0 Key dd 40 dup (0) conversion db '%d',0 lzUSER32 db 'USER32',0 lzWSPRINTFA db 'wsprintfA',0 offset1 dd 0044A6BCh patch1 dw 74h .data? ;========================================================================== ; Variables dont vous aurez besoin ;c'est variables, n'ayant pas par avance une taille définie, sont déclarées ; à part, pour que le compilateur leur réserve une place suffisante ;==========================================================================
hsmem dd ? smem dd ? nome dd ? hDlg dd ? cdc dd ? fmap dd ? faddr dd ? lg_serial DWORD ? serial db 10 dup (?) ; place réservée pour serial : 10 octets buffer db 90 dup (?) buffer1 db 20 dup (?) Directory db 512 dup (?) hFile HANDLE ? ; Handle du fichier. hInstance HINSTANCE ? hSkinMain_Back HANDLE ? hbSkinMain_Back HANDLE ? startinfo STARTUPINFO <> ; déclaration des structures pi PROCESS_INFORMATION <> DBEvent DEBUG_EVENT <> ofn OPENFILENAME <> hInstance HINSTANCE ? ; très important, c'est l'identifiant que votre ; application va se voir définir (en fonction ; des autres tâches) et qui permettra à l'OS ; de savoir qui est "propriétaire" des ; événements qui se produiront
Le paramètre hInstance est un nombre identifiant le programme en cours d'exécution. hInstance signifie handle Instance, sachez qu'un handle est une sorte d'index dans un tableau contenant des caractéristiques. Ici Windows tient un tableau d'info sur le programme et notre numéro d'handle lui permet de savoir ou regarder dans son tableau pour avoir le numéro qu'on lui donne (on peut connaître le handle d'un programme, sous SoftIce, en faisant TASK pour trouver le nom de la tâche recherchée, puis HWND pour obtenir le tableau des différents événements qui s'y rattache)
:task TaskName SS:SP StackTop StackBot StackLow TaskDB hQueue Events Crackme# 0000:0000 006AB000 006B0000 39A6 39DF 0000 Spool32 0000:0000 0063E000 00640000 36D6 339F 0000 :hwnd Crackme# Window Handle hQueue SZ QOwner Class Name Window Procedure 06BC(1) 39DF 32 CRACKME#4 TForm1 (delphi) 5557:000000FE 0544(2) 39DF 32 CRACKME#4 Edit (1er champ) 5557:0000016C 0510(2) 39DF 32 CRACKME#4 Edit (2d champ) 5557:00000156 0B40(2) 39DF 32 CRACKME#4 Edit 5557:00000140 052C(1) 39DF 32 CRACKME#4 TApplication 5557:00000064
La notion de fenêtre est extrêmement large pour le programmeur sous Windows. En effet une fenêtre peut très bien être la fenêtre d'une application, mais aussi tout composant de cette fenêtre : edit box, text box, bouton, etc...
Lg_serial DWORD ? ; les DWORD ? sont des adresses réservées, ; de taille non définies, mais dont j'aurais ; besoin pour stocker mes informations, ; à la différence des DWORD de 8 bits.
La déclaration de structures permet de réserver un espace mémoire dans lequel les valeurs retrournées (au pluriel) par l'appel à certaines APIs pourront être stockées, et rappellées par des "." d'union comme mov ofn.lpstrFile, OFFSET Directory.
A titre d'exemple, la structure OPENFILENAME contient des information que les fonctions GetOpenFileName et GetSaveFileName utilisent pour initilaiser les boites de dialogues de "Open" ou "Save As". Après que l'utilisateur ait fermé la boite de dialogue, le system retourne les informations liées à son choix dans la structure définie:
-> initialisation de la structure OPENFILENAME ayant pour nom 'ofn'.
Elle définira les différentes propriétés de la boite permettant la récupération
du fichier CrackMe#4.exe
(voir traduction par Morgatte du tut 30 d'Izcelion, ainsi que pour tous les autres commentaires s'y rapportant)
Chacunes de ces strings vont être stockées à une adresse précise, lors de la compilation.
Elles doivent impérativement se terminer par un 0 (NULL Terminated String)
typedef struct tagOFN { // ofn DWORD lStructSize; HWND hwndOwner; HINSTANCE hInstance; LPCTSTR lpstrFilter; LPTSTR lpstrCustomFilter; DWORD nMaxCustFilter; DWORD nFilterIndex; LPTSTR lpstrFile; DWORD nMaxFile; LPTSTR lpstrFileTitle; DWORD nMaxFileTitle; LPCTSTR lpstrInitialDir; LPCTSTR lpstrTitle; DWORD Flags; WORD nFileOffset; WORD nFileExtension; LPCTSTR lpstrDefExt; DWORD lCustData; LPOFNHOOKPROC lpfnHook; LPCTSTR lpTemplateName; } OPENFILENAME; |
typedef struct _PROCESS_INFORMATION { // pi HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; } PROCESS_INFORMATION; ------------------------------------ typedef struct _DEBUG_EVENT { // de DWORD dwDebugEventCode; DWORD dwProcessId; DWORD dwThreadId; union { EXCEPTION_DEBUG_INFO Exception; CREATE_THREAD_DEBUG_INFO CreateThread; CREATE_PROCESS_DEBUG_INFO CreateProcessInfo; EXIT_THREAD_DEBUG_INFO ExitThread; EXIT_PROCESS_DEBUG_INFO ExitProcess; LOAD_DLL_DEBUG_INFO LoadDll; UNLOAD_DLL_DEBUG_INFO UnloadDll; OUTPUT_DEBUG_STRING_INFO DebugString; RIP_INFO RipInfo; } u; } DEBUG_EVENT; |
typedef struct _STARTUPINFO { // si DWORD cb; LPTSTR lpReserved; LPTSTR lpDesktop; LPTSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2; LPBYTE lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; } STARTUPINFO, *LPSTARTUPINFO; |
;========================================================================= ; fin de la déclaration des constantes et des variables, ; et début du programme ;=========================================================================
.code
start:
; Le programme doit obligatoirement commencer par START.
; Il deviendra l'EntryPoint de l'application
invoke GetModuleHandle, NULL ; on récupère le handle de l'application mov hInstance, eax ; différent à chaque nouveau lancement du prog
La première étape va être de récupérer
le handle
du programme. Pour cela on utilise la fonction GetModuleHandleA avec comme paramètre
0. Le handle correspondant au process principal.
Notez l'utilisation de la macro call GetModuleHandle, 0
Le handle est dans eax... mov hInstance, eax va le stocker.
Je rappelle qu'en retour d'une fonction win32, c'est dans eax que se trouve la valeur de retour.
En ce qui concerne les registres, eax, ecx et edx ne sont pas préservés lors des appels aux fonctions
de l'api win32. Prenez donc vos précautions lorsque vous utilisez ces registres...
Pour les registres ebx, edx, edi, esi et ebp, c'est le contraire, ils sont toujours préservés et
Windows attend de vous que vous en fassiez de même....
invoke DialogBoxParam,hInstance,ADDR dlgname,0,ADDR WndProc,0 ; appel de la DialogBox dont les caractéristiques ; sont définies dans la section RSRC invoke ExitProcess,eax
;========================================================================= ; DialogBoxParam va lancer la procédure principale, WndProc, dont le nom ; est obligatoirement suivi de PROC et doit se terminer par ENDP ;=========================================================================
WndProc PROC hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM , lpszSkinName:DWORD (est lié au fond d'écran de la box)
hWnd est l'handle de l'application, uMsg, wParam et lParam, sont des paramètres utilisés ou non par les messages:
Maintenant vous devez connaître les messages susceptibles
de vous être envoyés par Windows. Pour cela cherchez dans la documentation officielle (le win32.hlp)
la liste vous intéressant. Pour une fenêtre ce sera "Window messages", pour la souris "mouse
input messages" pour une boite de dialogue "dialog box messages" etc...
Ensuite, vous devez, dans votre fonction WindowProc, redéfinir la gestion du message et ensuite renvoyer
0 ou bien appeler la fonction DefWindowProc, qui fera un traitement standard du message. Ce système est
très pratique car il permet d'intercepter les messages du système et les redéfinir pour nous
servir. Par exemple, lorsque vous clickez sur la croix pour fermer une appli, le msg WM_CLOSE est envoyé,
qui par le traitement standard appelle la fonction DestroyWindow qui elle envoie le message WM_DESTROY à
la fenêtre provoquant la fermeture de l'application. Si vous redéfinissez le traitement du msg WM_CLOSE
dans votre appli en le prenant en charge dans WindowProc, vous pouvez empêcher la fermeture de l'appli ou
demander une confirmation...
Mais quant on parle de messages, ca ne s'arrêtent pas à la seule réception de messages...
Vous pouvez en envoyer, à l'aide de la fonction SendMessage. Ceci a de l'intérêt par exemple
lorsque vous ne voulez pas ré écrire un bout de code qui gérait un autre message il suffit
de vous envoyer le message qui exécutera le code en question. Vous pouvez aussi interagir avec les fenêtres,
par exemple, en envoyant le message WM_SETTEXT à une appli, avec en paramètre une chaîne de
caractères, vous pourrez changer le titre de l'appli, en l'envoyant à une boite d'édition,
vous modifierez le texte qu'elle contient
local r :RECT ; wc et msg sont des variables locales. Elles ne local p :POINT ; seront reconnues qu'au sein de WinMain PROC local hdc_db :DWORD ; les variables commencant local hdc_bmp :DWORD ; par h, seront des handles-filles local hm_bmp :DWORD local nSkinNum :DWORD local szNewSkin[MAX_PATH] :BYTE local szFullSkin[MAX_PATH] :BYTE
.IF uMsg == WM_INITDIALOG ; initialisation des données de la DialogBox LoadSingle Main_Back, TRUE ; appel à une des deux macros -> load le fond d'écran
dans eax se trouve l' "événement" associé à la dlgbox que Windows renvoi, ici 110 = WM_INITDIALOG, c-à-d la fenêtre est en train de se créer.
; load l'icon à partir des ressources (définie dans le fichier RSRC.RC) invoke LoadIcon,hInstance,IDI_ICON ; = 500 : IDentificateur de l'icone mov hIcon, eax invoke SendMessage,hWin,WM_SETICON,1,hIcon ; control handles invoke GetDlgItem,hWin,IDC_Edit1 ; récupère le handle mov hEdit1, eax ; des "fenêtres" à gérer invoke GetDlgItem,hWin,IDC_Edit2 ; au sein d'une dialogBox mov hEdit2, eax invoke GetDlgItem, hWin, ID_BACKUP mov dword ptr [hBACKUP], eax invoke SendMessage, hBACKUP, BM_SETCHECK, BST_CHECKED, 0 ; check la fenêtre Backup ; load le bitmap de la "vignette" invoke StaticImage,NULL,hWin,178,17,121,44,IDC_Static mov hStatImage, eax invoke LoadBitmap,hInstance,100 ; (ID = 100 définie dans les ressources) mov hBmp, eax ; place l'image dans le static control (vigentte) invoke SendMessage,hStatImage,STM_SETIMAGE,IMAGE_BITMAP,hBmp RET
; place l'image dans la Boite de dialogue (fond d'ecran) .elseif uMsg == WM_ERASEBKGND invoke GetClientRect ,hWin, addr r invoke CreateCompatibleDC ,wParam mov hdc_db, eax invoke CreateCompatibleBitmap ,wParam, r.right, r.bottom mov hm_bmp, eax invoke SelectObject, hdc_db, hm_bmp invoke FillRect, hdc_db, addr r, hbSkinMain_Back invoke GetClientRect, hWin, addr r invoke BitBlt,wParam, 0, 0, r.right, r.bottom, hdc_db, 0, 0, SRCCOPY ; traitement du texte (sans lui le texte apparaitrait dans de petits rectangles) .elseif uMsg==WM_CTLCOLORSTATIC mov eax, hEdit2 ; évite le traitement du champ edition 2 cmp lParam,eax je no_color invoke SetTextColor,wParam,Yellow invoke SetBkMode, wParam,1 ; (1 = transparent, 2 = opaque) invoke GetStockObject, NULL_BRUSH ; (fond du texte transparent) no_color: ret .ENDIF .IF uMsg == WM_COMMAND ; si un évenement survient à la boite de dialogue .if wParam == IDC_Exit ; sa gestion commence ici, comme par exemple invoke EndDialog,hWin,0 ; un clic sur le bouton "EXIT" invoke ExitProcess, NULL
EAX va renvoyer la valeur: 111 = WM_COMMAND , et déterminer le type d'évenement dans wParam. Sous Wdasm, on le voit ainsi:
* Referenced by a (U)nconditional or (C)onditiona1 Jump at Addresses: |:0040111E(U), :00401198(U), :004011A1(C) :004011CF 817DOC11010000 cmp dword ptr [ebp+0C], 00000111 :004011D6 0F859DOOOOOO jne 00401279 -> pas de message |
; enregistre le contenu de Key dans le ClipBoard
.elseif wParam == IDC_Copy invoke GlobalAlloc,GMEM_MOVEABLE+GMEM_DDESHARE,addr lg_serial mov hsmem,eax invoke GlobalLock,hsmem mov smem,eax invoke lstrcpy,smem,addr Key invoke GlobalUnlock,hsmem invoke OpenClipboard,hWin invoke EmptyClipboard invoke SetClipboardData,CF_TEXT,hsmem invoke CloseClipboard invoke GetDlgItem,hDlg,hEdit1 invoke SetFocus,eax .endif .elseif uMsg == WM_CLOSE invoke EndDialog,hWin,0
La fonction EndDialog détruit la fenêtre spécifiée. La fonction envoie un message pour que la fenêtre soit désactivée. La fonction détruit également le menu Window (s'il y en a), efface les messages dans la file d'attente, détruit les timers...
.ENDIF .IF wParam==IDC_Gen push 20 push offset Key call RtlZeroMemory ; remplit de 20 zéros les adresses de stockage des variables push 50 push offset serial call RtlZeroMemory invoke GetDlgItemTextA,hWin,103, ADDR buffer1,10 ; saisie le texte se trouvant dans le 1er champ pour le placer cmp eax, 06 ; dans l'adresse pointée par BUFFER, limité à 10 caractères. jl erreur mov [lg_first],eax lea edi,[offset buffer1]
La fonction GetDlgItemTextA copie le texte du champ pointé par hEdit1 (ID = 103) dans un buffer (défini dans .const).
La valeur de retour de cette fonction sera placée dans EAX et contiendra le nombre de caractères qui auront été saisi, non compris le 00 final (NULL Terminated String) qui clôture la fin d'une chaîne de caractères (string). La fonction se présente ainsi:
UINT GetDlgItemText( HWND hDlg, // handle de la DialogBox int nIDDlgItem, // identificateur LPTSTR lpString, // adresse du buffer int nMaxCount // taille maximale de la string à copier );Au moment du break, la pile (stack) ressemblera à ceci:
[ESP+10h] - contiendra nMaxCount [ESP+0Ch] - contiendra IpString [ESP+08h] - contiendra nIDDlgItem [ESP+04h] - contiendra hwnd [ESP+00h] - contiendra le Return EIP
;========================================================================= ; Key Generator pour le Crackme#4 ;========================================================================= xor ecx,ecx ; compteur movsx eax, byte ptr [edi] ; EDI pointe sur l'adresse du Name entré imul esi,eax,2 ; EAX = 1er caractère x 2, résultat dans ESI @@: inc ecx ; incrémente caractère suivant cmp ecx,5 ; est ca que 6 caractères ont été traités ? jg end_imul ; si oui -> quitte la boucle mov al, byte ptr [edi+ecx] ; charge caractère suivant imul eax,eax,2 ; multiplie sa valeur ASCII par 2 add esi,eax ; ajoute le résultat dans ESI jmp @B ; boucle end_imul: mov eax, dword ptr [lg_first] ; récupère la longeur du Name imul eax,eax,2 ; la multiplie par 2 add eax, esi ; ajoute le résultat dans ESI push eax ; transforme le résultat (DWORD) push offset conversion ; en une chaine de caractères push offset Key ; le résultat sera dans Key call wsprintfA ;========================================================================= ; Affichage divers ;========================================================================= ; affiche le serial généré dans le second champ
invoke SetWindowTextA,hEdit2,ADDR Key
La fonction SetWindowText change le texte du champ spécifié (the specified window's title bar).
BOOL SetWindowText( HWND hWnd, // handle de la fenêtre LPCTSTR lpString // adresse de la string qui devra être affichée ); ret erreur: invoke SetWindowTextA,hEdit2, ADDR error ; si moins de 6 caractères entrés ret .endif ; connection Internet .IF wParam==IDC_Url invoke ShellExecuteA,hWin,addr open_site,addr url,NULL,NULL,01 .endif ; Boite about .IF wParam==IDC_About invoke MessageBoxA,hWin,addr TextAbout,addr caption_msg,MB_OK
(voir texte de Morgatte sur les MessageBox)
.endif ; Patch du process .IF wParam==IDC_Patch cmp dword ptr [serial],0 ; pour éviter les plantages jne processrunning ; et afin que le programme ne cherche mov dword ptr [serial],offset retour ; pas deux fois à ouvrir le même process call memorypatch processrunning: invoke MessageBoxA, NULL, addr running,addr caption_msg, MB_OK retour: .endif ; Recherche d'un programme .IF wParam==IDC_Brownse call OpenDialog .endif ret WndProc endp ; fin de la procédure principale
;========================================================================= ; affichage BitMap ;========================================================================= StaticImage proc lpText:DWORD,hParent:DWORD, a:DWORD,b:DWORD,wd:DWORD,ht:DWORD,ID:DWORD invoke CreateWindowEx,WS_EX_STATICEDGE,\ ADDR StaticClass ,lpText,\ WS_CHILD or WS_VISIBLE or SS_BITMAP,\ a,b,wd,ht,hParent,ID,\ hInstance,NULL ret StaticImage endp ;========================================================================= ; Brownse ;=========================================================================
OpenDialog PROC hWin:DWORD push 512 ; place 500 octets Null push offset Directory ; dans l'espacé memoire call RtlZeroMemory ; réservé à Directory mov ofn.lStructSize, SIZEOF ofn ; initilaise la structure ofn mov ofn.lpstrFilter, OFFSET FilterString mov ofn.lpstrFile, OFFSET Directory mov ofn.nMaxFile, 512 mov ofn.Flags, OFN_FILEMUSTEXIST or \ OFN_PATHMUSTEXIST or OFN_LONGNAMES or\ OFN_EXPLORER or OFN_HIDEREADONLY
; ouvre la boite de dialogue donnant accés aux fichiers du HDD invoke GetOpenFileName, offset ofn ret OpenDialog ENDP ;========================================================================= ; Memory Patch ;========================================================================= memorypatch proc invoke GetStartupInfo,addr startinfo invoke CreateProcess, addr Directory, NULL, NULL, NULL, FALSE, \ DEBUG_PROCESS+ DEBUG_ONLY_THIS_PROCESS, NULL, NULL, \ addr startinfo, addr pi cmp eax,0 jne File_founded call OpenDialog cmp eax, FALSE jne @F invoke MessageBoxA, NULL, addr absent, addr caption_msg, MB_OK jmp close_all @@: invoke GetStartupInfo,addr startinfo invoke CreateProcess, addr Directory, NULL, NULL, NULL, FALSE, \ DEBUG_PROCESS+ DEBUG_ONLY_THIS_PROCESS, NULL, NULL, \ addr startinfo, addr pi File_founded: .while TRUE invoke WaitForDebugEvent, addr DBEvent, INFINITE .break .if DBEvent.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT .if DBEvent.dwDebugEventCode==CREATE_PROCESS_DEBUG_EVENT invoke WriteProcessMemory, DBEvent.u.CreateProcessInfo.hProcess, \ modifications -> offset1, addr patch1, 1, NULL .elseif DBEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT .if DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT invoke ContinueDebugEvent, DBEvent.dwProcessId, \ DBEvent.dwThreadId, DBG_CONTINUE .continue .endif .endif invoke ContinueDebugEvent, DBEvent.dwProcessId, \ DBEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED .endw close_all: invoke CloseHandle,pi.hProcess invoke CloseHandle,pi.hThread Go_out_memory: push dword ptr [serial] ret memorypatch endp
;========================================================================= ; Fin ;========================================================================= end start
Handle
Le terme 'handle' dans ce texte fait référence aux handles des objets gérés par le
kernel32.
A la base, chaque handle peut être fermé par CloseHandle(). Ceci n'inclus PAS les objets graphiques
(GDI) comme HPEN, HDC, HBITMAP etc, qui n'ont rien à voir avec le kernel32.
Un handle, dans le contexte d'un process, est supposé être un index dans une table dont l'entrée
contiendrait les informations concernant différents objets.
DialogBox1 |
handle |
||
Champ d’édition 1 |
handle |
||
Champ d’édition 2 |
handle |
Prenons l'exemple de ce CrackMe se présentant sous la forme d'une Boite de
dialogue à deux champs, l'un pour le nom, l'autre pour le serial.
L'application, dés son démarrage, va se faire attribuer un handle (identificateur hInstance) :
invoke GetModuleHandle, NULL mov hInstance, eax
Puis la fenêtre-objet (ici notre boite de dialogue) se voit à son tour attribuer un handle (identificateur hWnd):
INVOKE CreateWindowExA,WS_EX_WINDOWEDGE,\ ADDR ClassName,ADDR NameApp,\ WS_SYSMENU or WS_MINIMIZEBOX or WS_DLGFRAME or WS_BORDER\ or WS_CLIPCHILDREN or WS_CLIPSIBLINGS or WS_VISIBLE,\ 280,200,385,162,NULL,NULL,hInst,NULL mov hWnd,eax
Le handle de la DialogBox est en seconde position sur la pile, c'est la fenêtre_objet-propriétaire.
Si cette boite de Dialogue dépendait d'une autre fenêtre_objet, son handle serait celui d'une fenêtre_objet-fille.
Fenêtre 1 |
handle |
|||||
DialogBox1 |
handle |
|||||
Champ d’édition 1 |
handle |
|||||
Champ d’édition 2 |
handle |
Puis c'est au tour du premier champ d'édition (identificateur hwnedit1)
Invoke CreateWindowExA,WS_EX_CLIENTEDGE, ADDR EditClass, NULL,\ WS_CHILDWINDOW or ES_READONLY or WS_VISIBLE or\ ES_AUTOHSCROLL,12,80,261,23,hWnd,NULL,hInstance,NULL Mov hwndEdit1,eax
HWND CreateWindowEx DWORD dwExStyle, // style de fenêtre étendue LPCTSTR lpClassName, // pointe vers le nom de la class enregistrée LPCTSTR lpWindowName, // pointe vers le nom de la fenêtre DWORD dwStyle, // style de fenêtre int x, // cordonnée horizontale de la fenêtre int y, // cordonnée verticale de la fenêtre int nWidth, // largeur de la fenêtre int nHeight, // hauteur de la fenêtre HWND hWndParent, // handle de la fenêtre parent ou propriétaire HMENU hMenu, // handle du menu ou de l'identifiant de la //fenêtre fille (NULL dans ce cas précis) HINSTANCE hInstance, // handle de l'application LPVOID lpParam // pointe vers les données de la fenêtre de création
Etc…
La table des handles est un DWORD contenant le nombre de handles différents, suivi par un tableau [1] contenant
les identificateurs-handle des objets en question. Les identificateurs-handle sont des DWORD contenant le drapeau
d'accès à un pointeur vers l'objet situé en mémoire.
typedef struct _HANDLE_TABLE { DWORD cEntries; // Nombre de handles dans la table HANDLE_TABLE_ENTRY array[1]; // Tableau dont la taille est fixée par cEntries } HANDLE_TABLE, *PHANDLE_TABLE; typedef struct _HANDLE_TABLE_ENTRY { DWORD flags; // flags dépend du type d'objet concerné PVOID pObject; // Pointeur vers l'objet auquel se réfère le handle } HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;
;========================================================================= ; Fichier ressources: tracé de la DialogBox, ID des fenêtres, BitMap, Icon... ;=========================================================================
#include "\masm32\include\resource.h" dialogbox DIALOGEX MOVEABLE IMPURE LOADONCALL DISCARDABLE 200,100, 245, 133, 0 STYLE DS_MODALFRAME | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_POPUP CAPTION "DialogBox v 1.0" FONT 8, "MS Sans Serif", 700, 0 /*FALSE*/ BEGIN GROUPBOX "", 100, 6,6,106,48, 0, , 0 DEFPUSHBUTTON "Exit", 109, 174,69,51,14, 0, , 0 CONTROL "", 101, "Static", SS_BLACKFRAME, 118,10,121,44, , 0 GROUPBOX "", 102, 7,58,232,68, 0, , 0 EDITTEXT 103, 52,70,108,12, ES_LEFT | WS_TABSTOP, , 0 EDITTEXT 104, 52,89,108,12, ES_READONLY | ES_LEFT, , 0 LTEXT "Name", 106, 13,72,32,9, SS_CENTER, , 0 LTEXT "Serial", 107, 13,91,32,9, SS_CENTER, , 0 LTEXT "URL Site",108, 13,110,32,9, SS_CENTER, , 0 PUSHBUTTON "Generate",110, 174,88,51,14, 0, , 0 PUSHBUTTON "Copy", 111, 174,107,51,14, 0, , 0 PUSHBUTTON "About", 112, 18,18,31,11, 0, , 0 PUSHBUTTON "Browse", 113, 18,35,31,11, 0, , 0 PUSHBUTTON "Patch", 114, 70,18,31,11, 0, , 0 PUSHBUTTON "http://christal.ctw.net", 115, 52,108,108,12, , 0 AUTOCHECKBOX "Backup", 116, 65, 35, 40, 9 END 100 BITMAP MOVEABLE PURE LOADONCALL DISCARDABLE "chris2.bmp" 200 BITMAP MOVEABLE PURE LOADONCALL DISCARDABLE "main_b.bmp" 500 ICON MOVEABLE PURE LOADONCALL DISCARDABLE "icon2.ICO"
Greets to Izcelion , X-or & Syntax Error
Bonne journée
Christal