Se définir un but
Cela fait un petit temps que j'y pense, mais j'ai jamais eu le temps, ou plutôt jamais pris le temps pour faire ce petit Reverse. Le but de celui-ci est de pouvoir Désassembler un fichier directement par la ligne de commande ( sans passer par le menu "Open a File To Disassemble..". ). Il suffirait de faire "W32dsm89 C:\Command.Com " par exemple pour désassembler C:\Command.com, ou encore en mettant un raccourci de W32Dsm89.EXE dans le repertoire SendTo de Windows, clicker avec le bouton droit de la souris sur le fichier à désassembler et choisir Envoyer-Vers/W32Dsm89 dans le menu contextuel.
Se procurer qqes renseignements
Avant de commencer, il faut savoir avec quoi on part. Nous devons avant tout savoir les API's qui nous intéressent :
GetOpenFileNameA ;Qui
affiche la Common DialogBox pour choisir le fichier à désassembler
GetCommandLineA
;Qui renvoit l'adresse de la ligne de commande
Nous devons aussi savoir l'adresse de la WinProc s'occupant de WDasm. Pour cela, on recherche les appels à DispatchMessageA dans W32Dasm89.EXE :
*
Reference To: USER32.DispatchMessageA, Ord:0000h
|
:004919E0 E8EDD50100
Call 004AEFD2
*
Reference To: USER32.DispatchMessageA, Ord:0000h
|
:004944C4 E809AB0100
Call 004AEFD2
*
Reference To: USER32.DispatchMessageA, Ord:0000h
|
:004A5611 E8BC990000
Call 004AEFD2
On trouve 3 références mais en mettant un bpx sur ces adresses on s'aperçois qu'il y en a qu'une qui est correcte :
:004944C3 53 push ebx
*
Reference To: USER32.DispatchMessageA, Ord:0000h
|
:004944C4 E809AB0100
Call 004AEFD2
On trouvera donc facilement la WindowProc en faisant BPX 004944C4 IF @(EBX+04)==0111, ensuite F5 et on click sur n'importe quelle fonction du menu. Normallement Softice break, le IF @(EBX+04)==0111 vérifie en fait si on est en présence d'un WMsg == WM_COMMAND. Il suffit maintenant de faire BPR 401000 4944C4 R suivit de F5. Ainsi, si la WindowProc se trouve entre 00401000 et 004944C4 elle va être exécutée et elle fera Breaker Softice. On trouve donc que la WindowProc se trouve en 004910E2 :
:004910E2 55
push ebp ;<-- On Break Ici !
:004910E3 8BEC
mov ebp, esp
:004910E5 FF7514
push [ebp+14]
:004910E8 FF7510
push [ebp+10]
:004910EB FF750C
push [ebp+0C]
:004910EE 50
push eax
:004910EF E807000000
call 004910FB
:004910F4 83C410
add esp, 00000010
:004910F7 5D
pop ebp
:004910F8 C21000
ret 0010
On doit aussi savoir l'ID du MenuItem "Open File To Disassemble..". Pour cela, il suffit de mettre un BPX 004910E2 IF @(esp+08)==0111, ensuite F5 et clicker sur le MenuItem en question. Si tout va bien, on break sous Sice. Un DD ESP donne l'etat de la pile qui contient le WMsg :
017F:0078C30C BFF7363B
00000944 00000111 00005F0C ;6..D........_..
017F:0078C31C 00000000
835A1997 0000017F 0078C33C ......Z....<.x.
Donc LParam = 00000000 WParam = 00005F0C et WMsg = 00000111. WParam contient l'ID de l'Item = 5F0C.
Maintenant recherchons l'appel à GetOpenFileNameA :
:0043E23A 8B9333E16300
mov edx, dword ptr [ebx+0063E133]
:0043E240 C60200
mov byte ptr [edx], 00
:0043E243 C78394EB01004C000000
mov dword ptr [ebx+0001EB94], 0000004C
:0043E24D 8B8B8C116F00
mov ecx, dword ptr [ebx+006F118C]
:0043E253 8B410C
mov eax, dword ptr [ecx+0C]
:0043E256 898398EB0100
mov dword ptr [ebx+0001EB98], eax
:0043E25C 8B9337E16300
mov edx, dword ptr [ebx+0063E137]
:0043E262 8993A0EB0100
mov dword ptr [ebx+0001EBA0], edx
:0043E268 33C9
xor ecx, ecx
:0043E26A 898BA4EB0100
mov dword ptr [ebx+0001EBA4], ecx
:0043E270 8B8319E16300
mov eax, dword ptr [ebx+0063E119]
:0043E276 8983ACEB0100
mov dword ptr [ebx+0001EBAC], eax
:0043E27C 8B9333E16300
mov edx, dword ptr [ebx+0063E133]
:0043E282 8993B0EB0100
mov dword ptr [ebx+0001EBB0], edx
:0043E288 C783B4EB010050000000
mov dword ptr [ebx+0001EBB4], 00000050
:0043E292 8B8B33E16300
mov ecx, dword ptr [ebx+0063E133]
:0043E298 898BB8EB0100
mov dword ptr [ebx+0001EBB8], ecx
:0043E29E C783BCEB010050000000
mov dword ptr [ebx+0001EBBC], 00000050
:0043E2A8 8D8514FFFFFF
lea eax, dword ptr [ebp+FFFFFF14]
:0043E2AE 8983C0EB0100
mov dword ptr [ebx+0001EBC0], eax
:0043E2B4 89BBC4EB0100
mov dword ptr [ebx+0001EBC4], edi
:0043E2BA 8B932BE16300
mov edx, dword ptr [ebx+0063E12B]
:0043E2C0 8993C8EB0100
mov dword ptr [ebx+0001EBC8], edx
:0043E2C6 8D8B94EB0100
lea ecx, dword ptr [ebx+0001EB94]
:0043E2CC 51
push ecx
*
Reference To: COMDLG32.GetOpenFileNameA, Ord:0000h
|
:0043E2CD E8B20F0700
Call 004AF284
* Reference To: COMDLG32.GetOpenFileNameA, Ord:0000h
|
:004AF284 FF25104B4D00 Jmp dword ptr [004D4B10]
Je désassemble le fichier Psapi.dll. Ensuite je met un Breakpoint
sur le call et je désassemble W32Dsm89.EXE.
Voici
l'état de la Structure OPENFILENAME avant et après le call
:
017F:007AB120 0000004C 00000948 00000000 00E92B3C L...H.......<+..
Avant le Call :
DWORD
lStructSize; 0000004C
HWND
hwndOwner; 00000948
HINSTANCE
hInstance; 00000000
LPCTSTR
lpstrFilter; 00E92B3C
LPTSTR
lpstrCustomFilter; 00000000
DWORD
nMaxCustFilter; 00000000
DWORD
nFilterIndex; 0000000F
LPTSTR
lpstrFile; 00E927100; 0,"sapi.dll",0
DWORD
nMaxFile; 00000050
LPTSTR
lpstrFileTitle; 00E92710; 0,"sapi.dll",0
DWORD
nMaxFileTitle; 00000050
LPCTSTR
lpstrInitialDir; 0078BF58
LPCTSTR
lpstrTitle; 004BCBAF; "C:\WINDOWS\Bureau\W32Dasm v8.93 Fixed",0
DWORD
Flags; 00001006
WORD
nFileOffset; 0026
WORD
nFileExtension; 002C
LPCTSTR
lpstrDefExt; 00000000
DWORD
lCustData; 00000000
LPOFNHOOKPROC
lpfnHook; 00000000
LPCTSTR
lpTemplateName; 00000000
017F:007AB120 0000004C 00000948 00000000 00E92B3C L...H.......<+..
Après le Call :
DWORD
lStructSize; 0000004C
HWND
hwndOwner; 00000948
HINSTANCE
hInstance; 00000000
LPCTSTR
lpstrFilter; 00E92B3C
LPTSTR
lpstrCustomFilter; 00000000
DWORD
nMaxCustFilter; 00000000
DWORD
nFilterIndex; 0000000F
LPTSTR
lpstrFile; 00E92710; "W32dsm89.exe",0
DWORD
nMaxFile; 00000050
LPTSTR
lpstrFileTitle; 00E92710; "W32dsm89.exe",0
DWORD
nMaxFileTitle; 00000050
LPCTSTR
lpstrInitialDir; 0078BF58; "C:\WINDOWS\Bureau\W32Dasm v8.93 Fixed",0
LPCTSTR
lpstrTitle; 004BCBAF
DWORD
Flags; 00001806
WORD
nFileOffset; 0026
WORD
nFileExtension; 002F
LPCTSTR
lpstrDefExt; 00000000
DWORD
lCustData; 00000000
LPOFNHOOKPROC
lpfnHook; 00000000
LPCTSTR
lpTemplateName; 00000000
Idée intuitive de la situation
Maintenant il faut se demander ce que nous allons faire, comment nous allons procéder. Voici mon idée :
On attend le WMsg WM_SHOWWINDOW ( = 0x018 ). Lorsque celui-ci est envoyé à la WindowProc, on vérifie si il y a un paramètre passé par la CommandLine grâce à GetCommandLineA. Si c'est le cas, on Hook la fonction API GetOpenFileNameA. La HookProc mettra les paramètres relatifs au nom de fichier passé par la ligne de commande et enlèvera ensuite le hook sur GetOpenFileNameA. On envoit ensuite le Wmsg WM_COMMAND avec LParam = 00 et WParam = 00005F0C. On émule ainsi un click sur "Open File To Disassemble..". La procédure va donc ce déroulée normallement et arriver à GetOpenFileNameA, c'est notre HookProc qui va prendre le relais, modifier la structure OPENFILENAME, et redonner la main au programme après avoir enlevé le Hook. Le fichier va se désassembler et tout sera comme avant.
Mise en pratique
J'ai
mis un BPX 004910E2 IF @(ESP+08)==018 et j'ai remarqué qu'il y avait
4 Break avant l'affichage de WDasm.
J'utiliserai
donc un compteur et nous ferons nos modifications à la 4ème
passe.
:004910E2 55
push ebp ;<-- On Break Ici !
:004910E3 8BEC
mov ebp, esp
:004910E5 FF7514
push [ebp+14]
:004910E8 FF7510
push [ebp+10]
:004910EB FF750C
push [ebp+0C]
:004910EE 50
push eax
:004910EF E807000000
call 004910FB
:004910F4 83C410
add esp, 00000010
:004910F7 5D
pop ebp
:004910F8 C21000
ret 0010
On va modifier le call 004910FB par call Addr_Notre_proc. Il faut trouver un peu d'espace libre et non utilisé (vérifier avec un BPR !) :
:004AF2B4 00000000000000000000
BYTE 10 DUP(0)
...
:004AF3F4 00000000000000000000
BYTE 10 DUP(0)
Voici Notre_proc :
.004AF2B4: 837C240818
cmp d,[esp][00008],018 ;WMsg
= WM_SHOWWINDOW ?
.004AF2B9: 0F853C1EFEFF
jne .0004910FB
;Sinon on redonne la main
.004AF2BF: FE05F4F34A00
inc b,[0004AF3F4]
;on incrémente le cmpt
.004AF2C5: 803DF4F34A0004
cmp b,[0004AF3F4],004 ;4ème
passe ?
.004AF2CC: 0F85291EFEFF
jne .0004910FB
;Sinon on redonne la main
.004AF2D2: 803DF5F34A0000
cmp b,[0004AF3F5],000 ;Flag
pour savoir si on a déjà fait les modifs
.004AF2D9: 0F851C1EFEFF
jne .0004910FB
;Sinon on redonne la main
Rem : Il faut modifier les caracteristiques de la section pour autoriser l'acces en ecriture !!
On doit faire appel à GetCommandLineA qui n'est pas dans l'IAT. Il faut donc récupérer l'adresse avec GetProcAddress ainsi que l'Handle de Kernel32 par GetModuleHandleA.
*
Reference To: KERNEL32.GetModuleHandleA, Ord:0000h
|
:004AEC0C FF25B4464D00
Jmp dword ptr [004D46B4]
*
Reference To: KERNEL32.GetProcAddress, Ord:0000h
|
:004AEAD4 FF25E4454D00
Jmp dword ptr [004D45E4]
On écris les Strings que nous allons utiliser :
.004AF3D0: 4B 65
72 6E-65 6C 33 32-00 47 65 74-43 6F 6D 6D Kernel32 GetComm
.004AF3E0: 61
6E 64 4C-69 6E 65 41-00 00 00 00-00 00 00 00 andLineA
On peut à présent récupérer l'adresse de GetCommandLineA :
.004AF2E4: 50
push eax
;Sauve le registre
.004AF2E5: 68D0F24A00
push 0004AF2D0
;push addr "Kernel32"
.004AF2EA: FF15B4464D00
call GetModuleHandleA
.004AF2F0: 68D9F34A00
push 0004AF3D9
;push addr "GetCommandLineA"
.004AF2F5: 50
push eax
;Push Kernel32 Handle
.004AF2F6: FF15E4454D00
call GetProcAddress
.004AF2FC: FFD0
call eax
;Call GetCommandLineA
Maintenant, il faut isoler le paramètre. Si le paramètre est NULL, il faut redonner la main, sinon on peut hooker GetOpenFileNameA. Regardons le format de la CommandLine :
"D:\Crack\W32dsm89\Reversed.EXE" D:\CRACK\W32DSM89\PSAPI.DLL ;NULL Terminated String !
Bon, il suffit de rechercher le 1er espace ( = 0x20 ). Si le 1er caractère après est 0x00 c'est qu'il n'y a pas de paramètre.
.004AF2FE: 40
inc eax
;On se positionne après le premier '"'
.004AF2FF: 803822
cmp b,[eax],020
;Est-on sur un espace
.004AF302: 75FA
jne .0004AF2FE
;Sinon on boucle
.004AF304: 80780100
cmp b,[eax+01],000
;Si oui, Y a-t-il un paramètre ?
.004AF308: 7506
jne .0004AF310
;Si oui, on continue
.004AF30A: 58
pop eax
;Sinon On restaure EAX
.004AF30B: E9EB1DFEFF
jmp .0004910FB
;On redonne la main
Je sauve l'adresse et Hook la fonction GetOpenFileNameA avant d'envoyer le WMsg et redonner la main.
.004AF3F8: 00 00
00 00 ;Addr_Param
.004AF3FC: 00
00 00 00 ;Addr_GetOpenFileNameA Original
.004AF310: 83C002
add eax,001
;Le Param est en [eax+01]
.004AF313: A3F8F34A00
mov [0004AF3F8],eax
;On sauve l'adresse
.004AF318: A1104B4D00
mov eax,[0004D4B10]
;Eax = addr de GetOpenFileNameA
.004AF31D: C705104B4D0027F34A00
mov d,[0004D4B10],0004AF327 ;Et
on remplace par l'addr de l'HookProc
Rem : 004AF327 n'est pas l'addr de la hook_proc, il va donc falloire la changer quand nous connaîtrons la bonne addr.
Il
faut maintenant envoyer le WMsg WM_COMMAND ( 0x0111 ) avec LParam = 0 et
WParam = 00005F0C.
On
utilise pour cela SendMessageA :
*
Reference To: USER32.SendMessageA, Ord:0000h
|
:004AF12E FF25284A4D00
Jmp dword ptr [004D4A28]
Avec comme paramètre :
HWND
hWnd, // handle of destination window
UINT
Msg, // message to send
WPARAM
wParam, // first message parameter
LPARAM
lParam // second message parameter
L'handle de la fenêtre de destination se trouve en [ESP+28]. On a donc tout ce qu'il faut :
.004AF328: 6A00
push 000
;LParam
.004AF32A: 680C5F0000
push 000005F0C
;WParam
.004AF32F: 6811010000
push 000000111
;WMsg
.004AF334: FF742428
push d,[esp+028] ;Hwnd
.004AF338: FF15284A4D00
call SendMessageA
.004AF33E: E9B81DFEFF
jmp .0004910FB
;Et on redonne la main
.004AF343: 0000
add [eax],al
Donc notre Hook_proc va commencer en :004AF343. On peut modifier l'instruction en :004AF31D :
.004AF31D: C705104B4D0043F34A00 mov d,[0004D4B10],0004AF343 ;Et on remplace par l'addr de l'HookProc
Maintenant attaquons-nous à la Hook_proc. Il faut qu'elle modifie 5 éléments de la structure OPENFILENAME qui se trouve en [[ESP+04]] ou en ECX car, rappelez-vous :
:0043E2CC 51 push ecx ; Addr de la structure
*
Reference To: COMDLG32.GetOpenFileNameA, Ord:0000h
|
:0043E2CD E8B20F0700
Call 004AF284
Les éléments à modifier sont :
[Struc+1C]
LPTSTR lpstrFile; 00E92710; "W32dsm89.exe",0
[Struc+24]
LPTSTR lpstrFileTitle; 00E92710;
"W32dsm89.exe",0
[Struc+2C]
LPCTSTR lpstrInitialDir; 0078BF58;
"C:\WINDOWS\Bureau\W32Dasm v8.93 Fixed",0
[Struc+38]
WORD nFileOffset;
0026
[Struc+3A]
WORD nFileExtension;
002F
Voici la Hook_proc que je vous ai concocté :
.004AF343: A1F8F34A00
mov eax,[0004AF3F8] ;Récupère
l'Addr du Param
.004AF348: 803800
cmp b,[eax],000
;Recherche la fin du Param
.004AF34B: 7403
je .0004AF350
;Si c'est la fin on continue
.004AF34D: 40
inc eax
.004AF34E: EBF8
jmps .0004AF348
;Sinon on boucle
.004AF350: 48
dec eax
;On se positionne à la fin
.004AF351: 80385C
cmp b,[eax],05C
;On recherche le '\'
.004AF354: 75FA
jne .0004AF350
;Si c'est pas, on boucle
.004AF356: 40
inc eax
;Sinon on se positionne juste après
.004AF357: 2B05F8F34A00
sub eax,[0004AF3F8] ;Déplacement
/r au début du param
.004AF35D: 66894138
mov [ecx+038],ax
;nFileOffset = eax
.004AF361: 0305F8F34A00
add eax,[0004AF3F8] ;Addresse
du nom du fichier
.004AF367: 57
push edi
;Sauvegarde les registres
.004AF368: 56
push esi
.004AF369: 8B791C
mov edi,[ecx+01C] ;edi
= lpstrFile
.004AF36C: 8BF0
mov esi,eax
;esi = addr du nom du fichier
.004AF36E: A4
movsb
;On copie le nom dans lpstrFile
.004AF36F: 803E00
cmp b,[esi],000
.004AF372: 75FA
jne .0004AF36E
.004AF374: A4
movsb
.004AF375: 4E
dec esi
;On recherche la position de l'extension
.004AF376: 803E2E
cmp b,[esi],02E
;On recherche donc le '.'
.004AF379: 75FA
jne .0004AF375
.004AF37B: 2B35F8F34A00
sub esi,[0004AF3F8]
.004AF381: 6689713A
mov [ecx+03A],si
;On sauve la position
.004AF385: 33C0
xor eax,eax
.004AF387: 668B4138
mov ax,[ecx+038]
;nFileOffset = longueur du path
.004AF38B: 51
push ecx
.004AF38C: 8B492C
mov ecx,[ecx+02C]
;lpstrInitialDir
.004AF38F: 87CF
xchg ecx,edi
;edi = lpstrInitialDir
.004AF391: 8B35F8F34A00
mov esi,[0004AF3F8] ;esi
= addr du param
.004AF397: F2A4
repne movsb
;met le path dans lpstrInitialDir
.004AF39B: C647FF00
mov b,[edi-01],000 ;Remplace
le '\' de fin par NULL
.004AF39F: 59
pop ecx
;Restaure les registres
.004AF3A0: 5E
pop esi
.004AF3A1: 5F
pop edi
.004AF3A2: A1FCF34A00
mov eax,[0004AF3FC]
.004AF3A7: A3104B4D00
mov [0004D4B10],eax ;Enlève
le Hook sur GetOpenFileNameA
.004AF3AC: B801000000
mov eax,000000001 ;Met
1 dans EAX pour dire qu'un fichier est choisi
.004AF3B1: C3
retn
;Merci :)
Maintenant il faut régler les problèmes :) Le debuggage... Tout d'abord,
.004AF318: A1104B4D00
mov eax,[0004D4B10]
;Eax = addr de GetOpenFileNameA
.004AF31D: C705104B4D0027F34A00
mov d,[0004D4B10],0004AF327 ;Et
on remplace par l'addr de l'HookProc
J'ai oublié de sauver l'addresse de GetOpenFileNameA. Il suffit de mettre un call vers :004AF3B2 et de régler cela.
.004AF318: E895000000
call .0004AF3B2
...
.004AF3B2: A1104B4D00
mov eax,[0004D4B10]
.004AF3B7: A3FCF34A00
mov [0004AF3FC],eax
.004AF3BC: C3
retn
Ensuite, il dit que "Could Not Get File Handle". C'est surement parceque le répertoire courant n'est pas le répertoire du fichier que nous voulons désassembler. Pour corriger cela, il va falloir appeler SetCurrentDirectoryA avec lpstrInitialDir comme paramètre. On va mettre un jmp en :004AF39F :
*
Reference To: KERNEL32.SetCurrentDirectoryA, Ord:0000h
|
:004AEC36 FF25D0464D00
Jmp dword ptr [004D46D0]
.004AF39F: EB1C
jmps .0004AF3BD
...
.004AF3BD: 59
pop ecx
.004AF3BE: 5E
pop esi
.004AF3BF: FF712C
push d,[ecx+02C]
.004AF3C2: FF15D0464D00
call SetCurrentDirectoryA
.004AF3C8: EBD7
jmps .0004AF3A1
Voilà ce qui corrige ce problème. Un autre problème est maintenant visible. Si on désassemble un fichier de format reconnu par WDasm, il y a des erreurs d'exception et si, par contre, il ne connait pas le format, il désassemble le fichier mais l'affichage déconne. Pour corriger cela, je crois qu'il faut modifier le moment où on envoit le Message WM_COMMAND qui émule l'appuis sur le bouton "Open File To Disassemble..". Une idée, qui normallement réglera les 2 problèmes, serait d'utiliser un autre WMsg, par Exemple WM_PAINT ( = 0x0F ) pour détecter la fin de l'affichage de WDasm. Comme pour WM_SHOWWINDOW, WM_PAINT est envoyé 4 fois avant que WDasm soit totallement affiché. Il suffit donc de remplacer ainsi :
.004AF2B4:
837C240818
cmp d,[esp][00008],0F ;WMsg =
WM_PAINT ?
.004AF2B9: 0F853C1EFEFF
jne .0004910FB
;Sinon on redonne la main
.004AF2BF: FE05F4F34A00
inc b,[0004AF3F4]
;on incrémente le cmpt
.004AF2C5: 803DF4F34A0004
cmp b,[0004AF3F4],004 ;4ème
passe ?
.004AF2CC: 0F85291EFEFF
jne .0004910FB
;Sinon on redonne la main
.004AF2D2: 803DF5F34A0000
cmp b,[0004AF3F5],000 ;Flag pour
savoir si on a déjà fait les modifs
.004AF2D9: 0F851C1EFEFF
jne .0004910FB
;Sinon on redonne la main
Remarquez aussi que j'avais mis un flag pour vérifier qu'on avait déjà fait les modifs ( en effet, si WM_PAINT est envoyé FF fois, après le cmpt va boucler 00, 01, 02, 03 et 04 ce qui desassemblera à nouveau le fichier ). Il suffit donc de modifier ce flag après l'exécution de notre Hook_proc, par exemple :
Nous avions ceci :
.004AF3C4: FF15D0464D00
call SetCurrentDirectoryA
.004AF3CA: EBD5
jmps .0004AF3A1
On modifie ainsi :
.004AF3CA: EB1F
jmps .0004AF3EB
...
.004AF3EB: FE05F5F34A00
inc b,[0004AF3F5]
.004AF3F1: EBAE
jmps .0004AF3A1
En vérifiant bien de laisser le cmpt ( 004AF3F4 ) et le flag ( 004AF3F5 ) à 00 avant l'exécution.
.004AF3F3: 0000
.004AF3F5: 0000
Maintenant, tout est fonctionnel ! Evidemment, ce cours à été fait en même temps que le Reverse et c'est le pourquoi des erreurs et de la non-optimisation du code ! De cette manière, vous pouvez voir qu'il n'est pas toujours nécessaire de tout recommencer. Par ce Reverse, je vous ai montré aussi qu'il ne faut pas connaître les adresses des fonctions que nous voulons utiliser dans le Reverse et qu'en nous aidant d'artifices comme l'API Hooking, tout devient possible... Maintenant, laissez votre imagination s'envoler et modifiez ( améliorez ) tout les programmes qui ne vous sembleront pas ou mal fini, c'est le meilleurs moyen de progresser.
Un
merci tout spécial à Christal qui entretient si bien la scène
francophone... Courage et MERCI !
Merci
aussi à tout les crackers, crackeuses, non-crackeurs et non-crackeuses
sans
qui cela n'aurait jamais pu être possible.
Amicalement
TeeJi