Date de publication : 20 avril 2008 00h40

Auteur : BeatriX

3 . Le crackme.

Nous pouvons donc, en appliquant les trois techniques d'attaque présentées précédemment , proposer le code du crackme en clair. Il va sans dire que malgré l'aide indispensable de mon émulateur, il n'a pas été trivial de reconstituer ce code source. Les raisons de cette complexité sont multiples.

D'abord, l'obfuscation utilisée est résistante et utilise de l'obfuscation de type "constructions opaques" que nous aborderons dans la quatrième partie. Les algorithmes utilisés dans le back-stepping ont leur limite et ne sont pas encore capables de résoudre toutes les situations (mais ça viendra :) ).

Ensuite, un émulateur, comme son nom l'indique, "EMULE". Il fait donc semblant avec tout ce que ça comprend ! Certaines portions de code du crackme ont perturbé l'émulateur parce qu'il est bourré de failles. Je ne compte donc pas le nombre de corrections de bugs mineurs qu'il a fallu que je fixe pour que l'ému fonctionne correctement.

Enfin, le nombre de program points utilisés comme points de départ est assez conséquent (au bas mot, une bonne quarantaine) et comme cette phase est réalisée manuellement, c'est finalement assez laborieux à faire.

On peut également dire que si le binaire avait été beaucoup plus gros comme par exemple une application complète, il m'aurait été impossible de proposer le code en clair. Je rappelle que les techniques utilisées ici sont "locales" et ne permettent d'étudier de façons convenables que quelques centaines de milliers de lignes de code. Un programme de plusieurs centaines de millions d'instructions ne pourrait pas être vu dans son ensemble. A cela s'ajoute le fait que le nombre d'appels à l'api windows est très limité et ne concerne que des fonctions très documentées. Je n'imagine pas un binaire truffé de fonctions incompréhensibles qu'il faudrait émuler sans trop savoir quoi en tirer...

3.1 Le code original

Voici donc le code intégral de ce crackme nettoyé de sa couche de protection. Nous avons donc supprimé approximativement 99,6 % des instructions utilisées. Il ne reste plus qu'une petite DialogBox !

J'ai surligné en orangé les instructions obtenues par analyse différentielle ( program points potentiels) et si vous survolez le code avec le pointeur, vous verrez apparaître en jaune les instructions obtenues par Backward-Slicing et en vert les instructions obtenues par analyse du data-flow sur esp.

=============================================== 
=
=			PROC : 40AB81
=
===============================================
		0040AB81h:
		0040BBCF pushad 
		0040CEB5 push 47A1B1h
		0040E0E9 push dword ptr fs:[0h] <--- Installation d'un SEH
		0040F5EF mov dword ptr fs:[0h], esp

		00410903 push dword ptr [4937D8h]
		00412283 call 40103Ch =========> IsWindowEnabled 
		00415B51 push eax
		00417798 push eax
		00418C62 push dword ptr [4937D8h]
		0041A808 push eax
		0041B98D push dword ptr [4937B8h]
		0041D06F push eax
		0041E2C8 push dword ptr [4937C0h]
		0041FB80 mov ebp, 3h
		00420E30h:
		00421FCC call 40101Eh =========> EnableWindow : eax
		004232AD dec ebp
		004232AE jne 420E30h 
		00424760 pop eax
		00426036 cmp eax, 0h
		00426039 je 43264Ah
		00427C3B mov byte ptr [409078h], 0h
		00429082 push 409078h
		0042A304 push dword ptr [4937B8h]
		0042B5A1 call 40106Ch =========> SetWindowTextA :  
		0042C9EE mov eax, 409027h
		00430BDA jmp 46E064h
		0043264Ah:
		00433B68 push 0FFh
		00434DE6 push 409078h
		0043677B push dword ptr [4937B8h]
		00437D69 call 401036h =========> GetWindowTextA 
		; =============================================
		;
		;		Vérification du serial
		;
		; =============================================

		0043901A mov ebx, 6964654Ah
		0043A318 mov edi, 409078h 

	loc_0043B7ABh:
		0043C9D2 mov al, byte ptr [edi]
		0043E007 or al, al
		0043E009 je loc_449C3Ch

		0043F4FC xor ecx, ecx
		004406E9 mov cl, 8h

	loc_00441B72h:
		00442D3B xor bl, al
		004444FB ror ebx, 1h
		00445E7C dec ecx
		00445E7D jne loc_441B72h

		004472B9 inc edi
		0044859C jmp loc_43B7ABh

	loc_00449C3Ch:
		0044AD47 xor ebx, 0AB979C82h

		0044D23C div ebx <-------------------- doit déclencher une exception de type divide error #DE

		0046CCBB mov eax, 409040h <---- Perdu !

	loc_0046E064h: <---- Retour du handler du SEH

		0046F51E push eax
		00470921 push dword ptr [4937C4h]
		00471C0B push dword ptr [4937A0h]
		004731E0 call 401060h =========> SetDlgItemTextA 
		00476F16 popad 
		00478BA9 ret
=============================================== 
=
=			HANDLER : 47A1B1
=
===============================================
47A1B1h:
0047B3A6 pushad 
0047C9A0 lea edi, dword ptr [esp+24h]
0047F3C4 mov esi, dword ptr [edi+8h]
00481A2A cmp dword ptr [esi+0B8h], 44D23Ch
00481A34 jne 48C80Ah
00482AD0 mov dword ptr [esi+0B0h], 409148h <----- pour éviter une recherche de constante !!
00483FA4 mov dword ptr [esi+0B8h], 46E064h
00485383 mov eax, dword ptr [edi+4h]
00486614 mov dword ptr [esi+0C4h], eax <---- esp pointe vers exception_registration principale
004876FA sub dword ptr [esi+0B0h], 0ECh
004889A2 popad 
0048AD5D ret

loc_0048C80Ah:
0048DB92 popad 
0048EDD9 mov eax, 1h 
00490AD7 ret 
=============================================== 
=
=			WINPROC
=
===============================================

00494B9Bh:
00495F3F pushad 
0049761B mov edi, dword ptr [esp+24h] 
00498B37 mov esi, dword ptr [esp+28h]
00499B26 mov ebp, dword ptr [esp+2Ch]

0049AE89 cmp esi, 110h <-- WM_INITDIALOG
	0049AE8F je 4B4BE0h

0049BCF3 cmp esi, 111h <-- WM_COMMAND
	0049BCF9 je 527348h

0049CF40 cmp esi, 136h <-- WM_CTLCOLORDLG
	0049CF46 je 4EE32Ch

0049E2B9 cmp esi, 138h <-- WM_CTLCOLORSTATIC
	0049E2BF je 4F3207h

0049F749 cmp esi, 133h <-- WM_CTLCOLOREDIT
	0049F74F je 50D0D7

004A0817 cmp esi, 135h <-- WM_CTLCOLORBTN
	004A081D je 51E57Ch

004A22A2 cmp esi, 10h
	004A22A5 jne 4AC158h
	004A3C65 push 0h
	004A4DA7 push edi
	004A5E38 call 401024h =========> EndDialog 
004A7547h:
	004A8CAC xor eax, eax 
	004A9BA0 inc eax 
	004AADBD jmp 4AEB6Bh 
004AC158h:
	004AD362 xor eax, eax
004AEB6Bh:
	004AFF0A mov dword ptr [esp+1Ch], eax 
004B1824 popad
004B31A2 retn 10h

=============================================== 
=
=			WM_INITDIALOG
=
===============================================

	004B4BE0h:
	004B609B mov dword ptr [4937A0h], edi

	004B71E7 push 406A43h
	004B8AEC push dword ptr [4937A0h]
	004BA00B call 40106Ch =========> SetWindowTextA : YO-bfuscator_I :) 
	004BB9D2 push 67h
	004BCDA3 push dword ptr [49379Ch]
	004BE7C2 call 401042h =========> LoadBitmapA

	004C0443 mov dword ptr [4937A4h], eax

	004C147C push 68h
	004C28DB push dword ptr [49379Ch]
	004C3E20 call 401042h =========> LoadBitmapA

	004C4F6C mov dword ptr [4937A8h], eax

	004C5F3B push 66h
	004C712E push dword ptr [49379Ch]
	004C8677 call 401042h =========> LoadBitmapA

	004C97FF mov dword ptr [4937ACh], eax

	004CABB1 push 69h
	004CBFD2 push dword ptr [49379Ch]
	004CD2B6 call 401042h =========> LoadBitmapA

	004CE56F mov dword ptr [4937B0h], eax 

	004CF32D mov ebx, 4937B4h 

	004D0420h:
	004D17B2 push ebx
	004D3172 push dword ptr [ebx]
	004D4E31 push dword ptr [4937A0h]
	004D61B3 call 401030h =========> GetDlgItem
	004D73E1 pop ebx
	004D8DF7 add ebx, 4h
	004DA217 mov dword ptr [ebx], eax 

	004DCFCE cmp ebx, 4937DCh
	004DCFD4 jc 4D0420h

	004DF60A push dword ptr [49379Ch]
	004E0FEF call 401048h =========> LoadIconA

	004E2072 push eax
	004E323C push 0h
	004E4D64 push 80h
	004E5F26 push dword ptr [4937A0h]
	004E7333 call 40105Ah =========> SendMessageA : WM_SETICON

	004E81D2 push 409027h
	004E9558 push dword ptr [4937C4h]
	004EA87E push dword ptr [4937A0h]
	004EBC58 call 401060h =========> SetDlgItemTextA : Entrez un code valide... 
	004ECE49 jmp 4A7547h
=============================================== 
=
=			WM_CTLCOLORDLG
=
===============================================
	004EE32Ch:
	004EF93D push 4h
	004F0A77 call 401000h =========> GetStockObject
	004F1E6A jmp 4AEB6Bh

=============================================== 
=
=			WM_CTLCOLORSTATIC
=
===============================================
	004F3207h:
	004F411D push eax 
	004F55B5 mov eax, esp 
	004F69FD push 4h 
	004F7D5F push eax 
	004F8F07 push dword ptr [4937C8h] 
	004FA105 call 401036h =========> GetWindowTextA 
	004FBB6B pop eax
	004FCDE8 mov ebx, 33CC33h 
	004FDF60 cmp ax, 2D2Dh 
	004FDF64 jne 500E16h 
	004FF481 mov ebx, 0FFFF00h 
00500E16h:
	00501DDB push ebx 
	00503116 push ebp 
	00504582 call 40100Ch =========> SetTextColor 
	005056B8 push 1h 
	00506B03 push ebp 
	00507D27 call 401006h =========> SetBkMode 
	0050987F push 4h 
	0050AA72 call 401000h =========> GetStockObject 

	0050BDBB jmp 4AEB6Bh
=============================================== 
=
=			WM_CTLCOLOREDIT
=
===============================================

	0050D0D7h:

	0050E6D4 push dword ptr [4937B8h]
	0050F95B call 401066h =========> SetFocus 

	0051130D push 0FFFFh
	00512DAD push ebp
	0051485A call 40100Ch =========> SetTextColor

	00515F07 push 1h
	0051769B push ebp
	00518F4C call 401006h =========> SetBkMode

	0051A428 push 4h
	0051C188 call 401000h =========> GetStockObject

	0051D54B jmp 4AEB6Bh 

=============================================== 
=
=			WM_CTLCOLORBTN
=
===============================================
	0051E57Ch:
	0051F63B push dword ptr [4937D8h] 
	0052140B call 40103Ch =========> IsWindowEnabled
	0052488D call 538FD9h 
	00525BE3 jmp 4A7547h

=============================================== 
=
=			WM_COMMAND
=
===============================================
00527348h:
	005286DB cmp ebp, 1h 
	005286DE je 53225Bh 
	0052A1E8 cmp ebp, 2h 
	0052A1EB je 52D2A9h 
	0052BDB4 jmp 4AC158h 
0052D2A9h:
	0052EC77 push 0h 
	0052FC70 call 401054h =========> PostQuitMessage
	00531602 jmp 4A7547h

0053225Bh:
	00533B92 call 40AB81h <-------- Routine de vérification du serial
	00534D99 push dword ptr [4937D0h] 
	00536842 call 401066h =========> SetFocus
	00537E69 jmp 4A7547h

=============================================== 
=
=			PROC 538FD9h
=
===============================================

	00538FD9h:
	0053A4A5 push eax 
	0053B921 push dword ptr [4937D0h] 
	0053C945 call 40102Ah =========> GetDC
	0053DAD3 mov ebx, eax 
	0053EEAF mov eax, dword ptr [4937A4h]
	005403A0 cmp dword ptr [esp], 0h 
	005403A4 je 542EEFh 
	005416FB mov eax, dword ptr [4937ACh]

00542EEFh:
	005440E9 call 5527F0h 
	05457AF push dword ptr [4937D8h] 
	005473DC call 40102A  =========> GetDC
	0054854C mov ebx, eax
	00549884 mov eax, dword ptr [4937A8h]
	0054B4B8 cmp dword ptr [esp], 0 
	0054B4BC je 54D7D1h 
	0054C464 mov eax, dword ptr [4937B0h]
0054D7D1h:
	0054E80D call 5527F0h 
	0054FC2C pop eax
	00550E5A ret 

=============================================== 
=
=			PROC 5527F0h
=
===============================================
	005527F0h:
	00553A5F push 4h
	00554DF5 push 14h
	00556357 push 36h
	00557F7A push 0h
	005591D1 push 0h
	00559EAC push 0h
	0055B0B7 push eax
	0055C482 push 0h
	0055D5FA push 0h
	0055E7E5 push ebx 
	0055FACC call 401018h =========> DrawStateA 
	0056151E ret 


=============================================== 
=
=			MAIN 
=
===============================================

005689A6 push 5801CFh
00569E8D push dword ptr fs:[eax]
0056B83C mov dword ptr fs:[eax], esp

0056C763 push 0h
0056DE51 call 401078h =========> GetModuleHandleA : Main

0056EED9 mov dword ptr [49379Ch], eax

00570171 push 0h
00571861 push 494B9Bh
00572C0A push 0h
00573C0E push 65h
00574E46 push eax
005761FC call 401012h =========> DialogBoxParamA
0057B92A push eax
0057D469 call 401072h =========> ExitProcess
=============================================== 
=
=			HANDLER : 5801CF
=
===============================================
005801CFh:
005819C4 pushad 
00582AB8 lea edi, dword ptr [esp+24h]
00583ACF mov esi, dword ptr [edi]
00587EE1 cmp dword ptr [esi], 80000003h <---- STATUS_BREAKPOINT
00587EE7 je loc_592F7Bh

005892FD push 0h
0058A5CB push 403BF9h <-- Erreur Fatale :(
0058BACD push 40530Ah <-- Une Erreur fatale a tout fait planter.
0058CD26 push 0h
0058E590 call 40104Eh =========> MessageBoxA
0058FE74 push 0h
00591ACC call 401072h =========> ExitProcess
loc_592F7Bh:
005943FB mov eax, dword ptr [edi+0B8h]
00595F0C cmp byte ptr [eax], 0CCh
00595F0F jne 598B98h
00596F43 inc dword ptr [edi+0B8h]
598B98h:
0059A257 popad  
0059CA27 ret


3.2 Remarques et explications

La première remarque qui vient est que le code source est composé pour moitié de code retrouvé par analyse différentielle. C'est indubitablement la grosse faille de ce crackme. Si vous survolez le code précédent, vous constatez qu'il est morcelé et que finalement, les routines sont assez éparsses. Ceci fait partie du plan pour rendre le code obscure. J'ai précisé plus haut que YoleJedi avait eu recours à deux techniques d'obfuscation : l'insertion de code et le réagencement du code. Une fois l'insertion de code supprimée, il nous reste le problème du réagencement. Ici, ce n'est pas un problème puisque le code reste relativement clair dans cet état.

Pour ce qui est du crackme à proprement parlé, c'est en somme une dialogbox dont la WinProc, après un calcul savant sur le serial , doit générer une exception de type #DE pour passer la main au handler du SEH qui se chargera d'afficher le message "GAGNE". La routine de vérification du serial n'étant pas une bijection (relation one-one) car elle n'est pas injective (il n'y a pas unicité du serial), j'ai opté pour le bruteforce. Il existe une quantité industrielle de serials comme vous le montre ce keygenerator. Cependant, YoleJedi m'a précisé que le serial "un code valide..." était fonctionnel.

On note que dans le handler chargé d'afficher le message de succès, l'adresse réelle de ce message n'est pas utilisée directement afin d'éviter une attaque par recherche de constante.

On note également qu'il y a un autre handler d'un autre SEH. Ce dernier est mis en place assez tôt. Il se charge juste d'assurer la solidité du crackme en affichant un "Erreur Fatale" au cas où il se passe quelque chose d'imprévu. Cependant, avant d'afficher ce message, ce handler vérifie que l'exception générée n'est pas issue d'un breakpoint posé à l'aide de l'opcode CCh. Si ça se trouvait être le cas, EIP serait incrémenté ce qui amènerait fatalement le binaire à avoir un comportement imprévisible. Au mieux, le binaire ne tient pas compte du BP, au pire, il crashe. Cette protection permet au binaire de se prémunir du "just in time debugging". YoleJedi serait-il un adepte de SoftIce ?

Pour le reste du code, il suffit de s'y pencher quelques minutes pour comprendre. Le binaire gère l'affichage de sa fenêtre en fonction des actions de l'utilisateur. Je n'entre pas dans le détail ici.

Copyright (C)- FRET (2008)

Page suivante

Valid XHTML 1.0 Strict

Valid CSS!