Date de publication : 20 avril 2008 00h40
Auteur : BeatriX
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...
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
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)