HITB WMD CTF level 5
Par Baboon le mercredi, novembre 11 2009, 22:37 - Lien permanent
On continue donc avec une série de crack-mes windows proposés dans le cadre du HITB WMD CTF, nous verrons juste comment résoudre les crack-mes 4 et 5 (les crack-mes 2 et 3 étant un peu trop ... bizarres, sans possibilité de vérifier les résultats et le 1er je ne l'ai tout simplement pas regardé.
On commence donc aujourd'hui par le crack-me 5 et une fois n'est pas coutume, je vais tenter d'expliquer le plus clairement possible ma démarche pour l'étape de l'unpacking en espérant que cela encouragera les novices à s'y mettre
Unpacking du binaire
Je commence tout de suite par préciser que cette étape n'est pas du tout nécessaire. Si vous avez la chance d'avoir le crack-me qui se lance chez vous (ce qui n'était pas le cas chez moi ...) vous avez (presque) directement accès au code du crackme. Malgré tout comme je me suis embêter à unpacker le binaire autant m'embêter à vous expliquer comment, de plus ca me donne l'occasion de publier dans un cadre légal mon superbe call fixer
Des l'ouverture du binaire sous olly on se rend compte qu'il est protégé par asprotect, ceux qui ont déja eu affaire à asprotect comprendront pourquoi ... Bref, après avoir un peu pesté sur les organisateurs du ctf qui utilisent des packers commerciaux j'ai commencé à unpacker la bête. Heureusement seul la protection des imports est utilisée ici, il est donc très rapide de retrouver l'oep de la cible et de fixer l'exécutable (ce qui n'aurait pas été le cas en cas de virtualisation à outrance du code, protection custom de asprotect que je n'ai pas encore réussit à casser pour le moment :'( ).
Une dernière chose, si le binaire est exécuté plus de 5 ou 10 fois, il refuse de se lancer et nous affiche une jolie MessageBox "OMG CTF OVERLORD JUST PRAWNED J00, NO MOAR RUNZ" "Unregistered Version", pour pouvoir le relancer je ne me suis pas trop embêté et ai utilisé Trial Reset (un petit outil bien pratique permettant comme son nom l'indique de remettre à 0 les compteurs de plusieurs packers)
Retrouver l'OEP
L'OEP (ou Original Entry Point, c'est à dire l'entry point du programme avant qu'il ne soit protégé pour les novices en unpacking) se retrouve très facilement en utilisant la méthode bien connue de remontée de la pile (vous verrez c'est comme le planté de bâton, s'tout con).
C'est la méthode la plus simple pour retrouver un OEP sans se casser la tête, elle ne fonctionne que pour les packers qui ne détruisent pas les premières instructions du programme, elle consiste tout simplement à lancer le programme (sous olly ou alors normalement puis en s'attachant au programme) puis à le mettre en pause (ici on attendra qu'il se termine tout simplement), il suffit alors de regarder dans la pile la succession d'appels qui ont été fait et de retrouver où la première fonction a été appelée, à partir de là il suffit de remonter un peu dans le code pour retrouver l'OEP.
0012FED4 ]0012FEE8 0012FED8 |00401764 RETURN to ctf_bin5.00401764 from ctf_bin5.00401627 <- 2ème fonction dans notre call stack 0012FEDC |FFFFFFFF 0012FEE0 |00000000 0012FEE4 |00000000 0012FEE8 \0012FF6C 0012FEEC 004012F6 RETURN to ctf_bin5.004012F6 from ctf_bin5.00401753 <- ici se trouve notre call 0012FEF0 FFFFFFFF 0012FEF4 42887815 0012FEF8 01A2009E RETURN to 01A2009E <- correspond à un call du protector dans la mémoire allouée, ce n'est donc pas notre première fonction 0012FEFC BB10C334
Ainsi dans notre cas, nous voyons dans la pile que la première fonction à être appelée (dans notre call stack) l'est depuis l'adresse 004012F6. A cette adresse on reconnait clairement le code généré par VC il nous suffit donc de remonter un peu dans le code et de chercher le jmp long sautant sur le début de la fonction.
004012D2 0FB74D C8 MOVZX ECX,WORD PTR [EBP-38] 004012D6 EB 03 JMP SHORT 004012DB 004012D8 6A 0A PUSH 0A 004012DA 59 POP ECX 004012DB 51 PUSH ECX 004012DC 50 PUSH EAX 004012DD 56 PUSH ESI 004012DE 68 00004000 PUSH 00400000 004012E3 E8 18FDFFFF CALL <ModuleEntryPoint> 004012E8 8945 E0 MOV [EBP-20],EAX 004012EB 3975 E4 CMP [EBP-1C],ESI 004012EE 75 06 JNZ SHORT 004012F6 004012F0 50 PUSH EAX 004012F1 E8 5D040000 CALL 00401753 004012F6 E8 84040000 CALL 0040177F 004012FB 897D FC MOV [EBP-4],EDI
Nous trouvons donc que l'OEP se situe en 00401353 où nous retrouvons le début typique d'un code compilé avec VC :
00401353 . E8 04170000 CALL 00402A5C 00401358 .^ E9 78FEFFFF JMP 004011D5
Fixer les imports
Asprotect ne se contente pas de compresser / chiffrer le code de l'exécutable, il protège aussi ses imports en remplaçant ses call [API] par des call Asprotect | garbage instruction (garbage instruction étant une instruction de 1 byte destinée à obtenir la même taille de code qu'un call []). De plus il élimine de l'IAT certaines adresses ce qui complique encore un peu plus notre tâche :
; call redirigé : 004011E6 . 8D45 98 LEA EAX,[EBP-68] 004011E9 . 50 PUSH EAX 004011EA . E8 11EE6301 CALL 01A40000 ; 01A40000 pointe ici sur une zone mémoire allouée 004011EF . 97 XCHG EAX,EDI
00408008 7C8260C2 kernel32.FreeResource 0040800C 7C80A055 kernel32.LoadResource 00408010 A494E800 <- une adresse supprimée 00408014 CC2AE72B <- une autre ... 00408018 7C80BD09 kernel32.SizeofResource
Heureusement il est possible de retrouver ses adresse grâce à une méthode dont j'ai appris l'existence grâce à ulysse_31 (merci à toi ;)) sans cette méthode il est bien plus compliqué de reconstruire l'IAT. Asprotect lors de la protection de l'exécutable remplace les RVA de l'IAT par des CRC du nom des APIs correspondante, lors de l'exécution du code du loader, il va rechercher les adresses de ces APIs à l'aide le leur CRC et les placer dans l'IAT. L'erreur de Alexeï Solodovnikov a été d'écraser les adresses des APIs éliminée par une valeur aléatoire mais par un CRC modifié du nom de l'API, pour retrouver les adresses correspondantes il suffit donc de modifier légèrement le comportement du loader pour qu'ils retrouve les adresses de toutes les APIs et non uniquement celles qui devraient l'être, ce qui est fait à l'aide de l'ollyscript suivant (désolé pour les noms des variables ...) :
var HBPJMP var HBPCLEF var HBPOEP var GetProc var ROKXA var HBPMAGICLOOP var pourri var moisie gpa "VirtualAllocEx" , "kernel32.dll" mov va , $RESULT bphws va , "x" LOOPLOL: run cmp eip , va jne LOOPLOL findmem #5356578B55148B5D088DBDFAFEFFFF8BC24883E8020FB6308B451083E8020FB600# cmp $RESULT , 0 je LOOPLOL bphwc va mov HBPJMP , $RESULT add HBPJMP , 33 mov HBPCLEF , HBPJMP add HBPCLEF , 3A mov GETPROCADD , HBPJMP add GETPROCADD , 1A1 mov HBPOEP , 00401353 bphws HBPCLEF , "x" bphws HBPJMP , "x" bphws HBPOEP , "x" LOL: run cmp eip , HBPJMP jne LOL MEGALOOP: cmp eip , HBPJMP jne pasapi cmp !ZF , 0 jne paspourri mov moisie , ebx add moisie , 39 mov moisie , [moisie] and moisie , 0FF cmp moisie , esi jne vraimentpourri sti go GETPROCADD mov [edx] , GetProc sti run jmp MEGALOOP vraimentpourri: mov !ZF , 1 mov pourri , 1 sti run jmp MEGALOOP paspourri: mov pourri , 0 run jmp MEGALOOP pasapi: cmp eip , HBPCLEF jne pasjmp cmp pourri , 1 je bienpourri sti run jmp MEGALOOP bienpourri: sub ecx , 0A sti run jmp MEGALOOP pasjmp: cmp eip , HBPOEP jne BLEM bphwc HBPJMP bphwc HBPOEP bphwc HBPCLEF ret
Maintenant que nous avons une IAT toute propre, il nous faut fixer les call redirigés par asprotect. Pour ce faire j'ai développé un callfixer sous la forme d'une dll qui sera injectée dans le processus à l'aide de mon plugin IinjOlly. Cette DLL fonctionne grossièrement de cette façon :
- Recherche de 2 signatures donnant les 2 adresses auxquelles l'adresse de l'API apparait dans eax lors de la redirection
- Pose de 2 HBPs sur ces adresses à l'aide des APIs [Get/Set]ThreadContext (Asprotect fais des CRCs sur son propre code pour déchiffrer le noms des APIs, ils faut donc éviter de modifier son code
)
- Mise en place d'un VectoredExceptionHandler afin de récupérer les exceptions générées par nos HBPs avant les SEH
- Recherche des call Asprotect, saut sur ces calls
- Logiquement un de nos HBPs devrait intercepter l'exécution de la routine de redirection et nous donner l'adresse de l'API correspondante
- Recherche de l'API dans l'IAT, patch de l'instruction
Au final cela nous donne ca :
#include <windows.h> #define AsproSign "\x8B\xF8\x8B\x45\xF4\x03\xB8\xE0\x00\x00\x00\x8B\x45\xEC\x03\xC7\x03\x45\xD8\x89\x45\xEC\x8B\x45\xE8\x2B\xC7\x2B\x45\xD8\x89\x45\xE8\x33\xC0\x8A\x43\x01\x8D\x04\x40\x8B\x55\xF4\x8B\x54\x82\x68\x8B\x06" #define AsproSign2 "\xC6\x00\xCF\x8D\x45\x0C\x50\x8D\x45\xCC\x50\x8D\x45\xDC\x50\x8B\x45\xFC\x50\x8B\x45\xF8\x50\x8B\x45\x10\x50\x8B\x45\xF4\x50" #define debut_scan (PBYTE)0x0401000 #define fin_scan (PBYTE)0x00407FD0 #define debut_iat (PDWORD)0x00408000 #define fin_iat (PDWORD)0x000408114 #define FunCallSign1 0x438B1C75 #define FunCallSign2 0xFF5309EB #define GetProcSign1 0x8B0C558B #define GetProcSign2 0x053B0845 #define AddrCallRedir 0x04011EA PBYTE Redirection; PBYTE AddrAPI; PBYTE AddrAPI2; DWORD originalEsp; PBYTE SearchSign(unsigned char *Sign,DWORD SignSize) { MEMORY_BASIC_INFORMATION mbi; DWORD i,j,k; PBYTE Mem; mbi.BaseAddress = 0; while (VirtualQuery(mbi.BaseAddress,&mbi,sizeof(MEMORY_BASIC_INFORMATION))) { if ((mbi.State == MEM_COMMIT) && ((mbi.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE_WRITECOPY | PAGE_READONLY)) != 0)) { Mem = (PBYTE)mbi.BaseAddress; for (i=0;i <= mbi.RegionSize - SignSize ; i++) { k = 0; if (&Mem[i] != Sign) { for (j=0;j<SignSize;j++) if (Mem[i+j] != Sign[j]) break; else k++; if (k==SignSize) return &Mem[i]; } } } mbi.BaseAddress = (PBYTE)mbi.BaseAddress + mbi.RegionSize; } return 0; } DWORD __declspec(naked) getApiAddress(DWORD address) { __asm { pushad pushfd push FS:[0] mov originalEsp , esp jmp DWORD ptr [esp+0x2C] } } DWORD __declspec(naked) retour(DWORD address, PDWORD Destination , PDWORD JccType) { __asm { mov esp , originalEsp mov DWORD ptr [esp + 0x1C + 4 + 4] , eax ; eax = valeur de retour du handler ... pop dword ptr FS:[0] popfd popad retn } } LONG CALLBACK ExcHandler(PEXCEPTION_POINTERS ExceptionInfo) { if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP) { if ((ExceptionInfo->ExceptionRecord->ExceptionAddress == AddrAPI) || (ExceptionInfo->ExceptionRecord->ExceptionAddress == AddrAPI2)) { ExceptionInfo->ContextRecord->Eip = (DWORD)retour; ExceptionInfo->ContextRecord->Dr0 = (DWORD)AddrAPI; ExceptionInfo->ContextRecord->Dr7 = DR7flag(OneByteLength,BreakOnExec,LocalFlag,0); ExceptionInfo->ContextRecord->Dr1 = (DWORD)AddrAPI2; ExceptionInfo->ContextRecord->Dr7 |= DR7flag(OneByteLength,BreakOnExec,LocalFlag,1); return EXCEPTION_CONTINUE_EXECUTION; } else return EXCEPTION_CONTINUE_SEARCH; } else { return EXCEPTION_CONTINUE_SEARCH; } } void SetHBPonExec(DWORD address,int num) { CONTEXT c; c.ContextFlags = CONTEXT_DEBUG_REGISTERS; GetThreadContext(GetCurrentThread(),&c); *(PDWORD)((int)(&c.Dr0) + sizeof(DWORD)*num) = address; c.Dr7 |= DR7flag(OneByteLength,BreakOnExec,LocalFlag,num); SetThreadContext(GetCurrentThread(),&c); } void go(HINSTANCE hinstDLL) { PBYTE curseur; PDWORD IAT; Redirection = *((PBYTE*)(AddrCallRedir + 1)) + AddrCallRedir + 5; DebugBreak(); AddVectoredExceptionHandler(0,ExcHandler); AddrAPI = SearchSign(AsproSign,sizeof(AsproSign)-1); AddrAPI2 = SearchSign(AsproSign2,sizeof(AsproSign2)-1); if ((! AddrAPI) || (! AddrAPI2)) { DebugBreak(); return; } AddrAPI += 0x5B; AddrAPI2 += 0x12; SetHBPonExec((DWORD)AddrAPI,0); SetHBPonExec((DWORD)AddrAPI2,1); for (curseur = debut_scan ; curseur <= fin_scan ; curseur ++) { if (*curseur == 0xE8) { PBYTE destination = *((PDWORD)(curseur + 1)) + curseur + 5; if (destination == Redirection) { DWORD addr = getApiAddress((DWORD)curseur); BOOL fixed = FALSE; for (IAT = debut_iat ; IAT <= fin_iat ; IAT ++) { if (*IAT == addr) { *((PWORD)curseur) = 0x15FF; *((PDWORD*)(curseur + 2)) = IAT; fixed = TRUE; break; } } if (! fixed) DebugBreak(); } } } } BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: go(hinstDLL); break; case DLL_PROCESS_DETACH: break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; } return TRUE; }
Dans un soucis de clarté (apparemment ca s'écrit comme ca ...) j'ai supprimé pas mal de lignes qui sont spécifiques à certains cas qui ne sont pas présent dans notre programme donc n'espérez pas unpacker n'importe quel programme protégé par asprotect à l'aide de ce code (néanmoins vous aurez déjà une bonne base ;))
Il nous suffit maintenant de dumper notre programme avec LordPe et de reconstruire les imports avec ImpRec, là je ne vous ferai pas de dessins, le net regorge de tutos sur l'utilisation de ces outils.
Tous ca pour ca ?!
Maintenant que nous avons notre exécutable unpacké, nous allons pouvoir l'étudier sérieusement et voir pourquoi cette étape était finalement complètement facultative.
En effet après une rapide analyse du code on se rend compte que cet exécutable se contente d'extraire de ses ressources un autre exécutable et de le lancer. L'exécutable en question n'est même pas chiffré dans les ressources ...
Il est extrait dans un fichier caché nommé %s%s.exe (ce qui fait crasher ollydbg) puis lancé en utilisant CreateProcessA.
Il suffisait donc pour ceux chez qui le challenge se lançait de copier %s%s.exe ce qui leur économisait pas mal de temps (chez moi le crack-me ne fonctionne pas, CreateProcessA refusant apparemment de lancer un fichier dont le nom contient des % et renvoie une erreur ERROR_BAD_EXE_FORMAT (000000C1))
Une fois %s%s.exe terminé, le 1er binaire supprime le fichier caché et se termine lui aussi.
Table Table Table je vous aime
Nous passons donc à l'étude de %s%s.exe (que nous nous empressons de renommer pour pouvoir l'ouvrir sous olly).
Arrivé à l'entry point nous reconnaissons encore clairement le début d'un programme compilé avec VC :
004018F4 > $ E8 0BD70000 CALL 0040F004 004018F9 .^ E9 78FEFFFF JMP 00401776 004018FE > 8BFF MOV EDI,EDI [...]
à ceci près que le call pointe sur une fonction qui ne ressemble en rien à celle appelée normalement, on se rend compte aussi que cette fonction est appelée un peu partout dans le code :
0040F004 /$ 50 PUSH EAX 0040F005 |. 52 PUSH EDX 0040F006 |. E8 00000000 CALL 0040F00B 0040F00B |$ 58 POP EAX 0040F00C |. 05 3A000000 ADD EAX,3A 0040F011 |> 8B10 /MOV EDX,[EAX] 0040F013 |. 395424 08 |CMP [ESP+8],EDX 0040F017 |. 74 0B |JE SHORT 0040F024 0040F019 |. 05 08000000 |ADD EAX,8 0040F01E |. 85D2 |TEST EDX,EDX 0040F020 |. 74 0A |JE SHORT 0040F02C 0040F022 |.^ EB ED \JMP SHORT 0040F011 0040F024 |> 5A POP EDX 0040F025 |. 8B40 04 MOV EAX,[EAX+4] 0040F028 |. 870424 XCHG [ESP],EAX 0040F02B |. C3 RETN 0040F02C |> 8B10 /MOV EDX,[EAX] 0040F02E |. 395424 08 |CMP [ESP+8],EDX 0040F032 |. 74 07 |JE SHORT 0040F03B 0040F034 |. 05 08000000 |ADD EAX,8 0040F039 |.^ EB F1 \JMP SHORT 0040F02C 0040F03B |> 5A POP EDX 0040F03C |. 8B40 04 MOV EAX,[EAX+4] 0040F03F |. 8B00 MOV EAX,[EAX] 0040F041 |. 870424 XCHG [ESP],EAX 0040F044 \. C3 RETN
Après une rapide étude, on se rend vite compte que cette fonction redirige l'exécution vers la fonction d'origine à partir de l'adresse de retour, il suffit donc d'écrire un petit code qui se chargera de remplacer tout les call redirection par les calls originaux. Pour se faire (et pour changer ) j'ai écrit une petite dll qui une fois injectée scann l'exécutable à la recherche de call redirection et de call [ptr redirection puis fixe chacun des calls, le code étant relativement peu volumineux je vous le met ici :
#include "main.h" #define debut_scan (PBYTE)0x0401000 #define fin_scan (PBYTE)0x0415FFF #define Redirection (PBYTE)0x0040F004 #define table ((PDWORD)0x040F045) #define table2 ((PDWORD)0x004101A5) DWORD getfunaddress(PBYTE address) { int i; for (i = 0; table[i] ; i+=2) if (table[i] == (DWORD)address+5) return table[i+1]; DebugBreak(); return 0; } DWORD getapiaddress(PBYTE address) { int i; for (i = 0; table2[i] ; i+=2) if (table2[i] == (DWORD)address+6) return table2[i+1]; DebugBreak(); return 0; } void DLL_EXPORT pouet() { PBYTE curseur; DebugBreak(); for (curseur = debut_scan ; curseur <= fin_scan ; curseur ++) { if (*curseur == 0xE8) { PBYTE destination = *((PDWORD)(curseur + 1)) + curseur + 5; if (destination == Redirection) *((PDWORD)(curseur + 1)) = getfunaddress(curseur) - (DWORD)curseur - 5; } else if ((*(PWORD)curseur == 0x15FF) && (*(PDWORD)(curseur + 2) == 0x40F000)) { *((PDWORD)(curseur + 2)) = getapiaddress(curseur); } } }
Je te vois petit sérial !
Une fois la dll injectée, l'exe modifié dumpé, nous pouvons passer à la vérification du sérial en lui même (enfin diront certains ...). On ouvre donc notre tout nouvel exe et on trouve rapidement que :
- l'exécutable affiche une jolie dialog box dont la dialog proc se trouve en 00401620
- que le sérial est vérifié en 004011F0 dès qu'un caractère est entré / modifié
- que la pression du bouton valider ne fait que regarder la valeur d'un byte en 40C6A0 pour afficher ou non le message de réussite.
Passons donc maintenant à l'étude de la fonction de vérification (en 004011F0 donc) plusieurs petites suprises nous attendent :
- double RDTSC, les valeurs de eax et ecx sont stockées et serviront surement plus tard ....
- vérification du nom de l'exécutable à l'aide de GetModuleFileNameW, remplacement d'un test eax , eax par un xor eax , eax
- exceptions (single step) elles servent juste de saut et sont donc facilement patchables ...
- CRC du code de vérification
Nous avons donc maintenant un exécutable parfaitement clean que l'on peut étudier avec IDA etc.
Au final on se rends compte que la vérification est juste un simple enchainement de vérifications d'équations de plusieurs variables (qui sont données par découpage du sérial en mots de 4 chars convertis en words ("01234567" => 0123, 4567) ) et qui doivent vérifier :
serial : h1 a b c d e f g h2 h1::h2 = hash(serial[4:32]) e = 4919 g-d % b = c f + 9838 = a 62336 - a = b g - 4919 == f f + g - 4919 = d g premier g composé de 5 7 et 8 g % 3 = 1 g % 99 = 64
Il suffit donc maintenant d'écrire un petit programme qui va se charger de retrouver ce sérial, le voici pour vous :
#include <stdio.h> #include <stdlib.h> unsigned char hexData[1032] = { 0x00, 0x00, 0x00, 0x00, 0x96, 0x30, 0x07, 0x77, 0x2C, 0x61, 0x0E, 0xEE, 0xBA, 0x51, 0x09, 0x99, 0x19, 0xC4, 0x6D, 0x07, 0x8F, 0xF4, 0x6A, 0x70, 0x35, 0xA5, 0x63, 0xE9, 0xA3, 0x95, 0x64, 0x9E, 0x32, 0x88, 0xDB, 0x0E, 0xA4, 0xB8, 0xDC, 0x79, 0x1E, 0xE9, 0xD5, 0xE0, 0x88, 0xD9, 0xD2, 0x97, 0x2B, 0x4C, 0xB6, 0x09, 0xBD, 0x7C, 0xB1, 0x7E, 0x07, 0x2D, 0xB8, 0xE7, 0x91, 0x1D, 0xBF, 0x90, 0x64, 0x10, 0xB7, 0x1D, 0xF2, 0x20, 0xB0, 0x6A, 0x48, 0x71, 0xB9, 0xF3, 0xDE, 0x41, 0xBE, 0x84, 0x7D, 0xD4, 0xDA, 0x1A, 0xEB, 0xE4, 0xDD, 0x6D, 0x51, 0xB5, 0xD4, 0xF4, 0xC7, 0x85, 0xD3, 0x83, 0x56, 0x98, 0x6C, 0x13, 0xC0, 0xA8, 0x6B, 0x64, 0x7A, 0xF9, 0x62, 0xFD, 0xEC, 0xC9, 0x65, 0x8A, 0x4F, 0x5C, 0x01, 0x14, 0xD9, 0x6C, 0x06, 0x63, 0x63, 0x3D, 0x0F, 0xFA, 0xF5, 0x0D, 0x08, 0x8D, 0xC8, 0x20, 0x6E, 0x3B, 0x5E, 0x10, 0x69, 0x4C, 0xE4, 0x41, 0x60, 0xD5, 0x72, 0x71, 0x67, 0xA2, 0xD1, 0xE4, 0x03, 0x3C, 0x47, 0xD4, 0x04, 0x4B, 0xFD, 0x85, 0x0D, 0xD2, 0x6B, 0xB5, 0x0A, 0xA5, 0xFA, 0xA8, 0xB5, 0x35, 0x6C, 0x98, 0xB2, 0x42, 0xD6, 0xC9, 0xBB, 0xDB, 0x40, 0xF9, 0xBC, 0xAC, 0xE3, 0x6C, 0xD8, 0x32, 0x75, 0x5C, 0xDF, 0x45, 0xCF, 0x0D, 0xD6, 0xDC, 0x59, 0x3D, 0xD1, 0xAB, 0xAC, 0x30, 0xD9, 0x26, 0x3A, 0x00, 0xDE, 0x51, 0x80, 0x51, 0xD7, 0xC8, 0x16, 0x61, 0xD0, 0xBF, 0xB5, 0xF4, 0xB4, 0x21, 0x23, 0xC4, 0xB3, 0x56, 0x99, 0x95, 0xBA, 0xCF, 0x0F, 0xA5, 0xBD, 0xB8, 0x9E, 0xB8, 0x02, 0x28, 0x08, 0x88, 0x05, 0x5F, 0xB2, 0xD9, 0x0C, 0xC6, 0x24, 0xE9, 0x0B, 0xB1, 0x87, 0x7C, 0x6F, 0x2F, 0x11, 0x4C, 0x68, 0x58, 0xAB, 0x1D, 0x61, 0xC1, 0x3D, 0x2D, 0x66, 0xB6, 0x90, 0x41, 0xDC, 0x76, 0x06, 0x71, 0xDB, 0x01, 0xBC, 0x20, 0xD2, 0x98, 0x2A, 0x10, 0xD5, 0xEF, 0x89, 0x85, 0xB1, 0x71, 0x1F, 0xB5, 0xB6, 0x06, 0xA5, 0xE4, 0xBF, 0x9F, 0x33, 0xD4, 0xB8, 0xE8, 0xA2, 0xC9, 0x07, 0x78, 0x34, 0xF9, 0x00, 0x0F, 0x8E, 0xA8, 0x09, 0x96, 0x18, 0x98, 0x0E, 0xE1, 0xBB, 0x0D, 0x6A, 0x7F, 0x2D, 0x3D, 0x6D, 0x08, 0x97, 0x6C, 0x64, 0x91, 0x01, 0x5C, 0x63, 0xE6, 0xF4, 0x51, 0x6B, 0x6B, 0x62, 0x61, 0x6C, 0x1C, 0xD8, 0x30, 0x65, 0x85, 0x4E, 0x00, 0x62, 0xF2, 0xED, 0x95, 0x06, 0x6C, 0x7B, 0xA5, 0x01, 0x1B, 0xC1, 0xF4, 0x08, 0x82, 0x57, 0xC4, 0x0F, 0xF5, 0xC6, 0xD9, 0xB0, 0x65, 0x50, 0xE9, 0xB7, 0x12, 0xEA, 0xB8, 0xBE, 0x8B, 0x7C, 0x88, 0xB9, 0xFC, 0xDF, 0x1D, 0xDD, 0x62, 0x49, 0x2D, 0xDA, 0x15, 0xF3, 0x7C, 0xD3, 0x8C, 0x65, 0x4C, 0xD4, 0xFB, 0x58, 0x61, 0xB2, 0x4D, 0xCE, 0x51, 0xB5, 0x3A, 0x74, 0x00, 0xBC, 0xA3, 0xE2, 0x30, 0xBB, 0xD4, 0x41, 0xA5, 0xDF, 0x4A, 0xD7, 0x95, 0xD8, 0x3D, 0x6D, 0xC4, 0xD1, 0xA4, 0xFB, 0xF4, 0xD6, 0xD3, 0x6A, 0xE9, 0x69, 0x43, 0xFC, 0xD9, 0x6E, 0x34, 0x46, 0x88, 0x67, 0xAD, 0xD0, 0xB8, 0x60, 0xDA, 0x73, 0x2D, 0x04, 0x44, 0xE5, 0x1D, 0x03, 0x33, 0x5F, 0x4C, 0x0A, 0xAA, 0xC9, 0x7C, 0x0D, 0xDD, 0x3C, 0x71, 0x05, 0x50, 0xAA, 0x41, 0x02, 0x27, 0x10, 0x10, 0x0B, 0xBE, 0x86, 0x20, 0x0C, 0xC9, 0x25, 0xB5, 0x68, 0x57, 0xB3, 0x85, 0x6F, 0x20, 0x09, 0xD4, 0x66, 0xB9, 0x9F, 0xE4, 0x61, 0xCE, 0x0E, 0xF9, 0xDE, 0x5E, 0x98, 0xC9, 0xD9, 0x29, 0x22, 0x98, 0xD0, 0xB0, 0xB4, 0xA8, 0xD7, 0xC7, 0x17, 0x3D, 0xB3, 0x59, 0x81, 0x0D, 0xB4, 0x2E, 0x3B, 0x5C, 0xBD, 0xB7, 0xAD, 0x6C, 0xBA, 0xC0, 0x20, 0x83, 0xB8, 0xED, 0xB6, 0xB3, 0xBF, 0x9A, 0x0C, 0xE2, 0xB6, 0x03, 0x9A, 0xD2, 0xB1, 0x74, 0x39, 0x47, 0xD5, 0xEA, 0xAF, 0x77, 0xD2, 0x9D, 0x15, 0x26, 0xDB, 0x04, 0x83, 0x16, 0xDC, 0x73, 0x12, 0x0B, 0x63, 0xE3, 0x84, 0x3B, 0x64, 0x94, 0x3E, 0x6A, 0x6D, 0x0D, 0xA8, 0x5A, 0x6A, 0x7A, 0x0B, 0xCF, 0x0E, 0xE4, 0x9D, 0xFF, 0x09, 0x93, 0x27, 0xAE, 0x00, 0x0A, 0xB1, 0x9E, 0x07, 0x7D, 0x44, 0x93, 0x0F, 0xF0, 0xD2, 0xA3, 0x08, 0x87, 0x68, 0xF2, 0x01, 0x1E, 0xFE, 0xC2, 0x06, 0x69, 0x5D, 0x57, 0x62, 0xF7, 0xCB, 0x67, 0x65, 0x80, 0x71, 0x36, 0x6C, 0x19, 0xE7, 0x06, 0x6B, 0x6E, 0x76, 0x1B, 0xD4, 0xFE, 0xE0, 0x2B, 0xD3, 0x89, 0x5A, 0x7A, 0xDA, 0x10, 0xCC, 0x4A, 0xDD, 0x67, 0x6F, 0xDF, 0xB9, 0xF9, 0xF9, 0xEF, 0xBE, 0x8E, 0x43, 0xBE, 0xB7, 0x17, 0xD5, 0x8E, 0xB0, 0x60, 0xE8, 0xA3, 0xD6, 0xD6, 0x7E, 0x93, 0xD1, 0xA1, 0xC4, 0xC2, 0xD8, 0x38, 0x52, 0xF2, 0xDF, 0x4F, 0xF1, 0x67, 0xBB, 0xD1, 0x67, 0x57, 0xBC, 0xA6, 0xDD, 0x06, 0xB5, 0x3F, 0x4B, 0x36, 0xB2, 0x48, 0xDA, 0x2B, 0x0D, 0xD8, 0x4C, 0x1B, 0x0A, 0xAF, 0xF6, 0x4A, 0x03, 0x36, 0x60, 0x7A, 0x04, 0x41, 0xC3, 0xEF, 0x60, 0xDF, 0x55, 0xDF, 0x67, 0xA8, 0xEF, 0x8E, 0x6E, 0x31, 0x79, 0xBE, 0x69, 0x46, 0x8C, 0xB3, 0x61, 0xCB, 0x1A, 0x83, 0x66, 0xBC, 0xA0, 0xD2, 0x6F, 0x25, 0x36, 0xE2, 0x68, 0x52, 0x95, 0x77, 0x0C, 0xCC, 0x03, 0x47, 0x0B, 0xBB, 0xB9, 0x16, 0x02, 0x22, 0x2F, 0x26, 0x05, 0x55, 0xBE, 0x3B, 0xBA, 0xC5, 0x28, 0x0B, 0xBD, 0xB2, 0x92, 0x5A, 0xB4, 0x2B, 0x04, 0x6A, 0xB3, 0x5C, 0xA7, 0xFF, 0xD7, 0xC2, 0x31, 0xCF, 0xD0, 0xB5, 0x8B, 0x9E, 0xD9, 0x2C, 0x1D, 0xAE, 0xDE, 0x5B, 0xB0, 0xC2, 0x64, 0x9B, 0x26, 0xF2, 0x63, 0xEC, 0x9C, 0xA3, 0x6A, 0x75, 0x0A, 0x93, 0x6D, 0x02, 0xA9, 0x06, 0x09, 0x9C, 0x3F, 0x36, 0x0E, 0xEB, 0x85, 0x67, 0x07, 0x72, 0x13, 0x57, 0x00, 0x05, 0x82, 0x4A, 0xBF, 0x95, 0x14, 0x7A, 0xB8, 0xE2, 0xAE, 0x2B, 0xB1, 0x7B, 0x38, 0x1B, 0xB6, 0x0C, 0x9B, 0x8E, 0xD2, 0x92, 0x0D, 0xBE, 0xD5, 0xE5, 0xB7, 0xEF, 0xDC, 0x7C, 0x21, 0xDF, 0xDB, 0x0B, 0xD4, 0xD2, 0xD3, 0x86, 0x42, 0xE2, 0xD4, 0xF1, 0xF8, 0xB3, 0xDD, 0x68, 0x6E, 0x83, 0xDA, 0x1F, 0xCD, 0x16, 0xBE, 0x81, 0x5B, 0x26, 0xB9, 0xF6, 0xE1, 0x77, 0xB0, 0x6F, 0x77, 0x47, 0xB7, 0x18, 0xE6, 0x5A, 0x08, 0x88, 0x70, 0x6A, 0x0F, 0xFF, 0xCA, 0x3B, 0x06, 0x66, 0x5C, 0x0B, 0x01, 0x11, 0xFF, 0x9E, 0x65, 0x8F, 0x69, 0xAE, 0x62, 0xF8, 0xD3, 0xFF, 0x6B, 0x61, 0x45, 0xCF, 0x6C, 0x16, 0x78, 0xE2, 0x0A, 0xA0, 0xEE, 0xD2, 0x0D, 0xD7, 0x54, 0x83, 0x04, 0x4E, 0xC2, 0xB3, 0x03, 0x39, 0x61, 0x26, 0x67, 0xA7, 0xF7, 0x16, 0x60, 0xD0, 0x4D, 0x47, 0x69, 0x49, 0xDB, 0x77, 0x6E, 0x3E, 0x4A, 0x6A, 0xD1, 0xAE, 0xDC, 0x5A, 0xD6, 0xD9, 0x66, 0x0B, 0xDF, 0x40, 0xF0, 0x3B, 0xD8, 0x37, 0x53, 0xAE, 0xBC, 0xA9, 0xC5, 0x9E, 0xBB, 0xDE, 0x7F, 0xCF, 0xB2, 0x47, 0xE9, 0xFF, 0xB5, 0x30, 0x1C, 0xF2, 0xBD, 0xBD, 0x8A, 0xC2, 0xBA, 0xCA, 0x30, 0x93, 0xB3, 0x53, 0xA6, 0xA3, 0xB4, 0x24, 0x05, 0x36, 0xD0, 0xBA, 0x93, 0x06, 0xD7, 0xCD, 0x29, 0x57, 0xDE, 0x54, 0xBF, 0x67, 0xD9, 0x23, 0x2E, 0x7A, 0x66, 0xB3, 0xB8, 0x4A, 0x61, 0xC4, 0x02, 0x1B, 0x68, 0x5D, 0x94, 0x2B, 0x6F, 0x2A, 0x37, 0xBE, 0x0B, 0xB4, 0xA1, 0x8E, 0x0C, 0xC3, 0x1B, 0xDF, 0x05, 0x5A, 0x8D, 0xEF, 0x02, 0x2D, 0x3C, 0x75, 0x6E, 0x6B, 0x6E, 0x6F, 0x77, 0x6E }; int isPrime(int a) { int i; if (!(a & 1)) return 0; for(i = 3; i*i < a ; i++) if (!(a % i)) return 0; return 1; } int isGoodComposed(int a) { int i; static const char tab[] = {0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,0,0}; for (i = 0; i < 4 ; i++) if (! tab[(a >> (i << 2)) & 0xF]) return 0; return 1; } unsigned int Hash(char *a1, int a2) { unsigned int i; char *v3; int v4; v4 = a2; v3 = a1; for ( i = 0xFFFFFFFF; v4; ++v3 ) { i = ((int*)hexData)[(unsigned char)((char)i ^ *v3)] ^ (i >> 8); --v4; } return ~i; } int main() { int a , b ,c ,d , e ,f , g; int hash; char serial[39]; for (g = 64 ;(g < 0xFFFF) && ((g % 3 != 1) || (! isPrime(g)) || (! isGoodComposed(g))) ; g += 99); if (g >= 0xFFFF) { return -1; } //printf("g : %04X\n",g); e = 4919; f = g - e; d = f + g - e; a = f + 2*e; b = 62336 - a; c = g-d % b; sprintf(serial,"%04X%04X%04X%04X%04X%04X%04X",a,b,c,d,e,f,g); hash = Hash(serial,7*4); }
On le lance et ... BOUM on a notre sérial : 20518BBC67C415ADCA9C1337654E7885A02C
Voila voila !
Je me rends compte que la fin de mon post est un peu rapide mais bon, j'ai déjà bien écris et j'ai un peu la flemme d'expliquer la vérification du sérial qui au final n'avait que peu d'interet ... Néanmoins si vous avez des questions précises ou si vous désirez un des binaires du challenge modifié, n'hésitez pas
Commentaires
Coucou Baboon,
Sympa cette série d'articles très diversifiés :).
Allez hop, je fais mon curieux :
il va parler de quoi exactement l'article sur Armadillo 6 ?
A+
Salut uLysse
content de te voir par ici et merci pour le commentaire :D
L'article sur armadillo portera juste sur la reconstruction des imports, rien de transcendant mais j'espère que ca sera quand même un minimum instructif :p
Sinon je t'annonce en avant première (petit veinard
) que je devrais sortir un nouveau post sur starforce dans les mois à venir...
Héhé, je crois savoir...
Le tool dont tu avais parlé avec Ivanlef0u et qui devait monitorer les hook de l'IDT est prêt ?
J'suis prêt à mettre ma main au feu :p
Ah tient non même pas :D
retire ta main de là uLysse !
Fin bref tu verras
sympa ton post. C'est effectivement bien décevant qu'ils utilisent un packeur commercial.