Dialog Box :
Initiation à l'Assembleur et au Reverse Engineering
For Newbies Only By christal
|
Avant Propos : Dans le texte qui suit, j'ai cherché à mieux comprendre comment fonctionnaient les Boites de Dialogues, à travers différentes approches, dans l'idée de pouvoir plus facilement les reverser le cas échéant. Pour cela j'ai écrit un petit programme avec un simulacre de protection, qui m'a permis de mieux appréhender les APIs DialogBoxParam, MessageBox, SendMessage, GetDlgItemTextA, EndDialog et SetWindowText. Et à fin de ce texte, je me suis essayé à reverser ma propre boite de dialogue... Mais qu'est qu'une DialogBox? Ce n'est ni plus ni moins qu'une fenêtre ordinaire qui fonctionne avec des
"child window controls", des objets "enfants" de fenêtres (les boutons, les champs de
saisie de textes...). La procédure DialogBox communiquera avec ses enfants par le jeu de messages, fonction
des événements qui lui arriveront. Name Virt Size Virt Offset Raw Size Raw Offset Characteristics .text 00000158 00001000 00000200 00000400 60000020 .rdata 0000010E 00002000 00000200 00000600 40000040 .data 00000060 00003000 00000200 00000800 C0000040 .rsrc 00000218 00004000 00000400 00000A00 C0000040 Traduisons un peu : .text Virtual Address 00001000 > + image base = adresse
Virtual Size 00000158 > taille réelle
Raw Data Offset 00000400 > emplacement différent Ram et HDD
Raw Data Size 00000200 > taille avec Padding
Characteristics 60000020 > Code Executable et Readable
Cette section contient les codes du programme en tant que tel. .rdata Virtual Address 00002000
Virtual Size 0000010E
Raw Data Offset 00000600
Raw Data Size 00000200
Characteristics 40000040 > Initialized Data, Readable
Ici, les import functions sont au beau milieu d'autres données. Souvent la section des imports s'appelle .idata .data Virtual Address 00003000
Virtual Size 00000060
Raw Data Offset 00000800
Raw Data Size 00000200
Characteristics C0000040 > Initialized Data, Readable, Writeable
La section .data va rassembler les variables déclarées : .data dlgname db "TESTWIN",0 > nom de la boite de dialogue hInstance dd 0 > déclaration de variable Num1 db "Unregistered version",0 > Titre d'une messagebox Num2 db "Version Non Enregistrée!",0 > idem Num3 db "Registered version",0 > idem Statut byte 0 > flag Utilisateur Registered .data? buffer db 16 dup(?) > taille allouée pour le Buffer de saisie des entrées clavier .rsrc Virtual Address 00004000
Virtual Size 00000218
Raw Data Offset 00000A00
Raw Data Size 00000400
Characteristics C0000040 > Initialized Data, Readable, Writeable
Section des ressources (Icônes, boites de dialogues, Strings table...). c'est
ici que se trouvent les informations qui vont permettre au programme d'afficher la DialogBox (Template). +++++++++++++++++ DIALOG INFORMATION ++++++++++++++++++ Number of Dialogs = 1 (decimal) Name: TESTWIN, # of Controls=000, Caption:"Dialog", ClassName:"" Cette boite de dialogue a été crée ainsi : TESTWIN DIALOG 0, 2244, -28472, 10
STYLE DS_ABSALIGN | WS_ICONICPOPUP | WS_MINIMIZE | WS_VISIBLE | WS_DISABLED |
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_MAXIMIZE | WS_CAPTION | WS_VSCROLL | WS_HSCROLL |
WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
CAPTION "Dialog"
MENU "\n\nÍz"
{
}
Dialog: TESTWIN Edit 100 > 1er Champ à remplir (name) Edit 102 > 2ème Champ à remplir (Comp) PushButton: Register 1000 > 1er bouton PushButton: Exit 1001 > second bouton Static: Name 20 > Texte " Name " Static: Comp 30 > Texte " Comp " Static: Serial 50 > Texte " Serial " Static: > Cadre pour faire joli Static: > Cadre vide en face de " Serial " Les 100, 102 etc… sont les identificateurs des objets crées tel qu'ils seront
reconnu dans les codes assemblés, comme vous les verrez plus tard (Attention! 100 est en décimal.
L'identificateur apparaitra sous la fomre 64h). Window Handle hQueue SZ QOwner Class Name Window Procedure 0F74(1) 37B7 32 RESDLG #32770 (Dialog) 178F:000050B1 0B38(2) 37B7 32 RESDLG Edit (1er champ) 177F:00000CC6 0C00(2) 37B7 32 RESDLG Edit (2eme champ) 177F:00000CC6 0EFC(2) 37B7 32 RESDLG Button (Register) 178F:00001040 0C84(2) 37B7 32 RESDLG Button (Exit) 178F:00001040 0C7C(2) 37B7 32 RESDLG Static (Name) 178F:00005C20 0C74(2) 37B7 32 RESDLG Static (Comp) 178F:00005C20 0C64(2) 37B7 32 RESDLG Static (Serial) 178F:00005C20 0394(2) 37B7 32 RESDLG Static (Cadre 1) 178F:00005C20 0EE8(2) 37B7 32 RESDLG Static (Cadre 2) 178F:00005C20 Avec une informations utilisable : le Handle de chaque objet, mais qui ne pas le
même d'une session à l'autre. Directory Name VirtAddr VirtSize -------------------------------------- -------- -------- Import 00002024 0000003C Resource 00004000 00000260 Import Address Table 00002000 00000024 000000C0 5045 0000 4C01 0400 C31F D438 0000 0000 PE....4 sections 000000D0 0000 0000 E000 0F01 0B01 050C 0002 0000 ....size of code 000000E0 0008 0000 0000 0000 0010 0000 0010 0000 ................ 000000F0 0020 0000 0000 4000 0010 0000 0002 0000 . ....Image Base 00000100 0400 0000 0000 0000 0400 0000 0000 0000 ................ 00000110 0050 0000 0004 0000 0000 0000 0200 0000 .P... Image Size 00000120 0000 1000 0010 0000 0000 1000 0010 0000 ................ 00000130 0000 0000 1000 0000 0000 0000 0000 0000 ................ 00000140 2420 0000 3C00 0000 0040 0000 1802 0000 $ ..<....@...... Import Table : (ou plutôt image Import descriptor. Courtesy of El.CaRaCoL,
muy grand amigo) 00000680 0000 0000 9200 4469 616C 6F67 426F 7850 ......DialogBoxP 00000690 6172 616D 4100 B800 456E 6444 6961 6C6F aramA...EndDialo 000006A0 6700 0201 4765 7444 6C67 4974 656D 5465 g...GetDlgItemTe 000006B0 7874 4100 BB01 4D65 7373 6167 6542 6F78 xtA...MessageBox 000006C0 4100 1002 5365 6E64 4D65 7373 6167 6541 A...SendMessageA 000006D0 0000 5553 4552 3332 2E64 6C6C 0000 7500 ..USER32.dll..u. 000006E0 4578 6974 5072 6F63 6573 7300 1101 4765 ExitProcess...Ge 000006F0 744D 6F64 756C 6548 616E 646C 6541 0000 tModuleHandleA.. 00000700 4B45 524E 454C 3332 2E64 6C6C 0000 0000 KERNEL32.dll.... Et par Wdasm +++++++++++++++++++ IMPORTED FUNCTIONS ++++++++++++++++++ Number of Imported Modules = 2 (decimal) Import Module 001: USER32.dll Import Module 002: KERNEL32.dll +++++++++++++++++++ IMPORT MODULE DETAILS +++++++++++++++ Import Module 001: USER32.dll Addr:00002084 hint(0092) Name: DialogBoxParamA Addr:000020B4 hint(01BB) Name: MessageBoxA Addr:000020A2 hint(0102) Name: GetDlgItemTextA Addr:00002096 hint(00B8) Name: EndDialog Addr:000020C2 hint(0210) Name: SendMessageA Import Module 002: KERNEL32.dll Addr:000020EC hint(0111) Name: GetModuleHandleA Addr:000020DE hint(0075) Name: ExitProcess Le listing à proprement parler, désassemblé par Wdasm : //******************** Program Entry Point ******** :00401000 6A00 push 00000000 GetModuleHandle récupère l'adresse de l'ImageBase dans EAX et la place dans [00403008] * Reference To: KERNEL32.GetModuleHandleA, Ord:0111h
|
:00401002 E84B010000 Call KERNEL32.GetModuleHandleA
:00401007 A308304000 mov dword ptr [00403008], eax
invoke GetModuleHandle, NULL mov hInstance, eax Création de la DialogBox :
:0040100C 6A00 push 00000000 > NULL (dwInitParam)
:0040100E 682B104000 push 0040102B > WndProc (lpDialogFunc)
:00401013 6A00 push 00000000 > NULL (hWndParent)
* Possible StringData Ref from Data Obj ->"TESTWIN" (dlgname)
|
:00401015 6800304000 push 00403000 > dlgname (Template)
:0040101A FF3508304000 push dword ptr [00403008] > hInstance
* Reference To: USER32.DialogBoxParamA, Ord:0092h
|
:00401020 E809010000 Call USER32.DialogBoxParamA
:00401025 50 push eax
invoke DialogBoxParam,hInstance,ADDR dlgname,0,ADDR WndProc,0 (Voir les paramètres de la DialogBox dans le chapitre Documentation) * Reference To: KERNEL32.ExitProcess, Ord:0075h
|
:00401026 E821010000 Call KERNEL32.ExitProcess
invoke ExitProcess,eax :0040102B 55 push ebp :0040102C 8BEC mov ebp, esp :0040102E 817D0C10010000 cmp dword ptr [ebp+0C], 00000110 (WM_INITDIALOG) 00000110 est au centre de la gestion des DialogBox. C'est un excellent
indicateur du moment ou les événements liés à la boite de Dialogue vont commencer à
être traités (déplacement de la souris, entrées de textes, clics, appui sur ENTER…) .if uMsg == WM_INITDIALOG
:00401035 7526 jne 0040105D > va traiter le message
:00401037 EB11 jmp 0040104A > vous remarquerez qu'il n'y a pas
> d'adresse 0040104A !
szText dlgTitle,"Dialog Box Param" invoke SendMessage,hWin,WM_SETTEXT,0,ADDR dlgTitle En fait c'est une appréciation du débuggeur, et/ou du désassembleur, car les codes suivants correspondent au texte " Dialog Box param ", le titre de la DialogBox : :00401039 44 69 61 6C 6F 67 20 42-6F 78 20 50 61 72 61 6D Dialog Box Param :00401039 44 inc esp :0040103A 69616C6F672042 imul esp, dword ptr [ecx+6C], 4220676F :00401041 6F outsd :00401042 7820 js 00401064 :00401044 50 push eax :00401045 61 popad :00401046 7261 jb 004010A9 :00401048 6D insd :00401049 006839 add byte ptr [eax+39], ch :0040104C 104000 adc byte ptr [eax+00], al Les codes à l'adresse 0040104A commencent ainsi : 0040104A loc_0040104A:
:0040104A 6839104000 push 00401039 > pousse le texte (dlgTitle)
:0040104F 6A00 push 00000000 > NULL
:00401051 6A0C push 0000000C > hWin (handle de la Box)
:00401053 FF7508 push [ebp+08] > SendMessage
* Reference To: USER32.SendMessageA, Ord:0210h
|
:00401056 E8EB000000 Call USER32.SendMessageA
:0040105B EB1D jmp 0040107A
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401035(C) | :0040105D 817D0C11010000 cmp dword ptr [ebp+0C], 00000111 La valeur 00000111! Très importante à repérer, c'est à partir de celle ci que les messages qui nous intéressent le plus vont être traités (appuis sur les boutons, entrée de texte dans un champ, fermeture de l'application...) * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401042(C) | :00401064 7504 jne 0040106A > évènement lié à WM_Command? :00401066 7416 je 0040107E > oui -> va traiter le message .elseif uMsg == WM_COMMAND > comme pour les BMSG ! je Main_Command :00401068 EB10 jmp 0040107A * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401064(C) | :0040106A 837D0C10 cmp dword ptr [ebp+0C], 00000010 > = WM_Close :0040106E 750A jne 0040107A > Exit ou pas .elseif uMsg == WM_CLOSE :00401070 6A00 push 00000000 > NULL
:00401072 FF7508 push [ebp+08] > hWin
* Reference To: USER32.EndDialog, Ord:00B8h
|
:00401075 E8BA000000 Call USER32.EndDialog
invoke EndDialog,hWin,0 * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:0040105B(U), :00401068(U), :0040106E(C) | :0040107A C9 leave :0040107B C21000 ret 0010 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401066(C) | :0040107E 8B4510 mov eax, dword ptr [ebp+10] :00401081 663DE803 cmp ax, 03E8 > est ce le bouton Register qui a été sollicité ? :00401085 7563 jne 004010EA > sinon continu :00401087 6A10 push 00000010 > le buffer est limité à 16 caractères (Il ne faut pas confondre le nombre de caractères entrés au clavier de ceux qui vont être stockés dans la variable Buffer. C'est une façon indirecte de faire un cmp eax, 10 !) :00401089 6850304000 push 00403050 > adresse du Buffer où vont être stockées les entrées. :0040108E 6A64 push 00000064 > = 100 d. C'est l'identificateur du 1er champ :00401090 FF7508 push [ebp+08] > handle de la DialogBox (cf le tableau de SI) * Reference To: USER32.GetDlgItemTextA, Ord:0102h :00401093 E8A2000000 Call USER32.GetDlgItemTextA > saisie au clavier :00401098 803D5030400000 cmp byte ptr [00403050], 00 > pas d'entrée dans le champ 1? :0040109F 7415 je 004010B6 > va voir dans le champ 2 :004010A1 6A00 push 00000000
Et sinon affiche la messagebox " Unregistered Version ". :004010A1 6A00 push 00000000 > bouton simple (MB_OK)
* Possible StringData Ref from Data Obj ->"Unregistered version"
|
:004010A3 680C304000 push 0040300C > titre de la boite (Num 1)
:004010A8 6850304000 push 00403050 > texte entré dans le champ " Name "
:004010AD 6A00 push 00000000 > fenêtre concernée
(au cas où la messageBox serait attribuée à une fenêtre en particulier, ce qui n'est pas le cas ici.)
* Reference To: USER32.MessageBoxA, Ord:01BBh
|
:004010AF E88C000000 Call USER32.MessageBoxA
:004010B4 EB34 jmp 004010EA
invoke MessageBox,NULL,offset buffer,ADDR Num1,MB_OK jmp suite * Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040109F(C)
|
:004010B6 6A10 push 00000010 > limitation à 16 caractères
:004010B8 6850304000 push 00403050 > adresse du buffer
:004010BD 6A66 push 00000066 > identificateur du second champ
:004010BF FF7508 push [ebp+08] > handle de la dialogBox
* Reference To: USER32.GetDlgItemTextA, Ord:0102h
|
:004010C2 E873000000 Call USER32.GetDlgItemTextA
:004010C7 803D5030400000 cmp byte ptr [00403050], 00 > le Buffer est vide?
:004010CE 741A je 004010EA > continue
Si le second champ contient un texte, et seulement lui, la messageBox " registered version " va être affichée avec le texte que vous aurez entré dans le champ " Comp ", et un flag va être mis à 01. C'est le drapeau Utilisateur enregistré. :004010D0 6A00 push 00000000 > bouton OK
* Possible StringData Ref from Data Obj ->"Registered version"
|
:004010D2 683A304000 push 0040303A > Titre de la fenêtre
:004010D7 6850304000 push 00403050 > texte entré (limité à 16 caractères)
:004010DC 6A00 push 00000000 > handle de la fenêtre
* Reference To: USER32.MessageBoxA, Ord:01BBh
|
:004010DE E85D000000 Call USER32.MessageBoxA
:004010E3 C6054D30400001 mov byte ptr [0040304D], 01 > Flag Good Boy
invoke MessageBox,NULL,offset buffer,ADDR Num3,MB_OK mov Statut,01 * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:00401085(C), :004010B4(U), :004010CE(C) | :004010EA 663DE903 cmp ax, 03E9 > bouton Exit ? :004010EE 7404 je 004010F4 > Terminé ! :004010F0 C9 leave :004010F1 C21000 ret 0010
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004010EE(C)
L'utilisateur est il enregistré ?
:004010F4 803D4D30400001 cmp byte ptr [0040304D], 01 > 01 = Oui
:004010FB 740A je 00401107 > saute la MessageBox
* Possible StringData Ref from Data Obj ->"Version Non Enregistr"
Ici, la boite de Message n'est pas " immédiate " mais fait appel à une forme de macro :
|
:004010FD 6821304000 push 00403021 > ->Version Non Enregistrée
:00401102 E80E000000 call 00401115 > appel de la macro
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004010FB(C)
|
:00401107 6A00 push 00000000
:00401109 FF7508 push [ebp+08]
* Reference To: USER32.EndDialog, Ord:00B8h
|
:0040110C E823000000 Call USER32.EndDialog > détruit la DialogBox
:00401111 C9 leave
:00401112 C21000 ret 0010 > et Bye bye...
La macro qui s'occupe d'afficher la MessageBox: * Referenced by a CALL at Address:
|:00401102
|
:00401115 55 push ebp
:00401116 8BEC mov ebp, esp
:00401118 6A00 push 00000000 > bouton OK
* Possible StringData Ref from Data Obj ->"Unregistered version"
|
:0040111A 680C304000 push 0040300C > titre de la boite
:0040111F FF7508 push [ebp+08] > message
:00401122 6A00 push 00000000 > handle de la fenêtre
* Reference To: USER32.MessageBoxA, Ord:01BBh
|
:00401124 E817000000 Call USER32.MessageBoxA
:00401129 C9 leave
:0040112A C20400 ret 0004 > retour vers l'appelant
BoiteMessage proc Message:DWORD push MB_OK push offset Num1 push Message push 00 call MessageBoxA ret BoiteMessage endp
Ceci étant fait, il faut maintenant mettre la main sur la procédure
de gestion de cette DialogBox, et trouver le test lié à l'appui sur le bouton " Generate ".
Bien sur dans un aussi petit programme, rien n'est plus facile, mais essayons de procéder comme si nous
avions affaire à un 4 Mo !
Ensuite, Wdasm va nous la localiser dans le Dead Listing +++++++++++++++++ DIALOG INFORMATION ++++++++++++++++++ Number of Dialogs = 1 (decimal) Name: TESTWIN, # of Controls=000, Caption:"Dialog", ClassName:"" En double cliquant sur la StringData Ref qui lui correspond : * Possible StringData Ref from Data Obj ->"TESTWIN"
|
:00401015 6800304000 push 00403000
:0040101A FF3508304000 push dword ptr [00403008]
* Reference To: USER32.DialogBoxParamA, Ord:0092h
|
:00401020 E809010000 Call 0040112E
:00401025 50 push eax
Pour autant, à moins de faire une recherche empirique sur l'ensemble des 00000111 du listing, il serait souhaitable de mettre de suite la main sur la procédure de traitement des messages liés à DialogBoxParamA. :0040102E 817D0C10010000 cmp dword ptr [ebp+0C], 00000110 Une fois localisé l'adresse de 00000111 : :0063FB50 00000111 000003E8 00000FB8 8B96112F ............/... :0063FB60 00000187 0063FB78 BFF94407 3EB78BBC ....x.c..D.....> ou 03E8 est l'identificateur du bouton " Generate ". :0040107E 8B4510 mov eax, dword ptr [ebp+10] > 03E8 :00401081 663DE803 cmp ax, 03E8 > identificateur de Generate :00401085 7563 jne 004010EA > suite :00401087 6A10 push 00000010 > traitement A partir de l'adresse 004010EA, la routine de traitement de l'événement lié à l'appui du bouton Generate va commencer :00401087 6A10 push 00000010 > le buffer est limité à 16 caractères :00401089 6850304000 push 00403050 > adresse du Buffer :0040108E 6A64 push 00000064 > identificateur du 1er champ :00401090 FF7508 push [ebp+08] > handle de la DialogBox :00401093 E8A2000000 Call USER32.GetDlgItemTextA > saisie au clavier :00401098 803D5030400000 cmp byte ptr [00403050], 00 > pas d'entrée dans le champ 1 ? :0040109F 7415 je 004010B6 > va voir ce qui se passe dans le 2d champ C'est à dire, dans notre cas, la saisie du nom entré dans le premier
champ qui pourrait servir de base à la génération d'un sérial. :00404218 6850304000 PUSH 00403050 > le " bon sérial " :0040421D FF7508 PUSH DWORD PTR [EBP+08] > handle de la Box :00404220 E8AFD9B4BF CALL USER32!SetWindowTextA > affichage sérial :00404225 C6054D30400001 MOV BYTE PTR [0040304D],01 > drapeau Utilisateur =1 :0040422C E9B9CEFFFF JMP 004010EA > jump en Full Option.
|
int DialogBoxParam(
HINSTANCE hInstance, // handle de l'application instance
LPCTSTR lpTemplateName, // identifie le template de la dialog box
HWND hWndParent, // handle de la fenêtre propriétaire
DLGPROC lpDialogFunc, // pointe vers la procédure de la Dialog box
LPARAM dwInitParam // initialization value
);
hInstance |
int MessageBox(
HWND hWnd, // handle de la fenêtre propriétaire (en général 00)
LPCTSTR lpText, // adresse du message apparaissant dans la boite
LPCTSTR lpCaption, // adresse du Titre de la message box
UINT uType // style de la message box (type de bouton choisi...)
);
|
Bonne Journée