IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C Discussion :

Comment poser un hook pour un seul processus ?


Sujet :

C

  1. #1
    Futur Membre du Club
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 18
    Points : 9
    Points
    9
    Par défaut Comment poser un hook pour un seul processus ?
    Bonjour,
    Tous les exemples que j'ai trouvés jusqu'à présent montrent comment poser des hook "généraux" de type WH_KEYBOARD
    donc sur tous les processus chargés.
    Or, et à titre pédagogique, je souhaite placer un hook de type "WH_CALLWNDPROC" sur une seule fenêtre et dans un seul processus
    afin de connaître les messages qu'elle reçoit ( un peu comme SPY++ ).
    Et bien sûr, je n'y arrive pas. Ma dll de hook ne reçoit rien. Il n'y a pas d'erreur quand je trace en pas à pas le déroulement.
    Ma cible : processus = explorer, fenêtre = Shell_TrayWnd
    Pour poser le hook, je procède ainsi ( un extrait de mon code ):

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    // trouver la fenêtre pour laquelle je veux placer le hook
    HWND hShellTray = FindWindow(L"Shell_TrayWnd", NULL);
    // trouver son thread associé pour que le hook s'applique uniquement à ce thread
    DWORD tid = GetWindowThreadProcessId(hShellTray, &pid);
    // charger la dll de hook ( quel autre but que connaître l'adresse de la fonction de hook ? )
    HMODULE dll = LoadLibrary(lpFullPath);
    // obtenir l'adresse de la fonction de hook
    HOOKPROC addr = (HOOKPROC)GetProcAddress(dll, "meconnect");
    // appeler l'API pour poser ce hook
    hHook = SetWindowsHookEx(WH_CALLWNDPROC, addr, dll, tid);
    // attendre une action de l'utilisateur pour annuler le hook et décharger la dll ( peut on ne pas attendre ? )
     
    Ma dll de hook :
    //ne rien faire d'autre qu'écrire dans un fichier de log
    extern "C" __declspec(dllexport) int meconnect(int code, WPARAM wParam, LPARAM lParam) {
    	FILE *file;
    	LPCWPSTRUCT pt_stMessage = (LPCWPSTRUCT)lParam;
    	if (code >= 0 )
    	{
    		if (pt_stMessage->message == 0x579 && pt_stMessage->wParam == 3)
    		{
    			fopen_s(&file, "C:\\temp\\hook_function.txt", "a+");
    			//fprintf(file, "Function keyboard_hook called avec code=0x%x\n", code);
    			fprintf(file, "Function keyboard_hook called \n");
    			fclose(file);
    		}
    	}
    	return(CallNextHookEx(NULL, code, wParam, lParam));
    }

    Nouvel Edit : Avec windbg et si je l'attache au processus explorer.exe je ne vois pas ma dll de hook. A quel moment cette dll sera t-elle chargée par explorer.exe?
    Merci d'avance.

    Ps : je ne sais pas si la totalité du code peut vous être utile?
    Ps : je ne sais pas si je dois poster dans le forum C ou C++ ?

  2. #2
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Ta procédure de hook n'a pas la bonne convention d'appel. Et si tu lui donnes la bonne convention d'appel en gardant dllexport, le nom non-décoré ne sera plus trouvé par GetProcAddress() (mais le nom décoré devrait l'être).
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  3. #3
    Futur Membre du Club
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 18
    Points : 9
    Points
    9
    Par défaut
    Merci pour ta réponse.
    C'est vrai que je suis débutant et que je fais beaucoup de copier/coller.
    La déclaration de la procédure de hook vient de ce site :
    http://resources.infosecinstitute.co...on-on-windows/
    Et j'avoue ne pas savoir comment déclarer cette fonction.
    Si tu peux me donner des pistes, voire une solution/explication, ce serait super.
    Merci d'avance

    Ps : j'ai remplacé la cible "explorer.exe et la fenêtre Shell_TrayWnd" par un programme rudimentaire basé sur le même code en modifiant le nom de la classe et en neutralisant le code d'injection de hook. J'ai aussi modifiant le nom de la classe de fenêtre dans le programme qui pose le hook. Et avec ces modifications, je récupère bien les messages dans le fichier de log. A croire que explore.exe et cette fenêtreShell_TrayWnd ne veulent pas de mon hook.

  4. #4
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  5. #5
    Futur Membre du Club
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 18
    Points : 9
    Points
    9
    Par défaut
    Avec la déclaration actuelle, Depends.exe affiche le nom non décoré de la fonction de la dll de hook.
    Même avec cette déclaration, quand je change la cible ( comme je le signale dans le PS ci dessus ), la dll de hook reçoit bien les messages.
    Si je change la déclaration de la dll de hook par ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    extern "C" __declspec(dllexport) LRESULT CALLBACK meconnect(int code, WPARAM wParam, LPARAM lParam) {
    ...
    }
    alors depends.exe montre que la fonction de hook est exportée avec un nom décoré. Sans changer le nom de la fonction et recompiler le programme qui pose le hook, ce dernier ne trouve pas la fonction ( message d'erreur ). Donc sur ce point, je ne sais pas avancer.

    Mais, et je suis têtu, puisque la dll est bien chargée et écrit les info dans le log lorsque je prends une cible autre que explorer.exe et la fenêtre Shell_TrayWnd, il me semble que ce n'est pas un problème de déclaration, bien que ce que j'ai copié/collé ne respecte pas les règles du C ( ou C++ ).

    J'ai écrit un programme mode console. Le paramètre par défaut est la classe Shell_TrayWnd. On peut préciser éventuellement une autre classe en argument.
    Cela permet de vérifier que le hook fonctionne avec une autre cible.

    Avec explorer.exe et avec notepad.exe ( 64bits sous windows10 ), dll hook = NOK
    Avec un programme mode graphique(win32) ( issu d'un projet vide ) que je compile avec VSCommunity2013, dll hook = OK

    A tout hasard je place mes deux codes.
    Le programme mode console
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
     
    // ConsoleTest.cpp : Defines the entry point for the console application.
    //
     
    #include "stdafx.h"
    #include <Windows.h>
    #include <string>
    using namespace std;
     
    TCHAR szNameDllHook[] = TEXT("monHook.dll");
    TCHAR szNameWnd[] = TEXT("Shell_TrayWnd");
    HHOOK hHook = NULL;
     
    bool injection(LPCWSTR szShellTray)
    {
    	// recherche de la fenetre Shell_TrayWwd h=0x201d8 tid=680
    	HWND hShellTray = FindWindow(szShellTray, NULL);
    	if (hShellTray == 0)
    	{
    		wprintf(TEXT("\r\nImpossible de trouver la fenêtre cible %s \r\n"), szShellTray);
    		return false;
    	}
    	wprintf( TEXT("\r\n%s : 0x%x \r\n"), szShellTray, hShellTray);
     
    	// recherche du thread associé à cette fenetre
    	DWORD pid;
    	DWORD tid = GetWindowThreadProcessId(hShellTray, &pid);
    	wprintf(TEXT("\n\rpid : 0x%x et td : 0x%x \n\r"), pid, tid);
     
    	// trouver le répertoire de ce programme
    	TCHAR result[MAX_PATH];
    	DWORD dwRes;
    	dwRes = GetModuleFileName(NULL, (LPWSTR)result, MAX_PATH);
    	if (dwRes == 0)
    	{
    		wprintf(TEXT("\r\nImpossible de trouver le répertoire de ce programme. %x \r\n"), result);
    		return false;
    	}
     
    	// on concatène le chemin du prg et le nom de la dll de hook
    	wstring path(result);
    	wstring nameDll(szNameDllHook);
    	wstring pathDll;
    	pathDll = path.substr(0, path.find_last_of('\\') + 1);
    	pathDll += nameDll;
    	LPWSTR lpFullPath = (LPWSTR)(pathDll.c_str());
     
    	/*
    	* Load library in which we'll be hooking our functions.
    	*/
    	HMODULE dll = LoadLibrary(lpFullPath);
    	if (dll == NULL) {
    		//printf("The DLL could not be found.\n");
    		wprintf(TEXT("\r\nLa dll de Hook doit se trouver dans le même répertoire que ce programme.\r\n"));
    		return false;
    	}
     
    	/*
    	* Get the address of the function inside the DLL.
    	*/
    	HOOKPROC addr = (HOOKPROC)GetProcAddress(dll, "meconnect");
    	if (addr == NULL) {
    		wprintf(TEXT("\r\nErreur avec GetProcAddress : %x\r\n"),GetLastError());
    		return false;
    	}
    	wprintf(TEXT("\r\naddr meconnect : 0x%x \r\n"), addr);
     
    	/*
    	* Hook the function.
    	*/
    	//hHook = SetWindowsHookEx(WH_KEYBOARD, addr, dll, tid);
    	//hHook = SetWindowsHookEx(WH_GETMESSAGE, addr, dll, tid);
    	hHook = SetWindowsHookEx(WH_CALLWNDPROC, addr, dll, tid);
     
    	if (hHook == NULL) {
    		wprintf(TEXT("\r\nImposible d'injecter le Hook !\r\n"));
    		FreeLibrary(dll);
    		return false;
    	}
    	return true;
    }
     
    int _tmain(int argc, _TCHAR* argv[])
    {
    	if (injection(argc == 2 ? argv[1] : szNameWnd))
    	{
    		printf("Program successfully hooked.\nPress enter to unhook the function and stop the program.\n");
    		getchar();
    		UnhookWindowsHookEx(hHook);
    		return 0;
    	}
    	return 1;
    }
    et le code de la dll :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
     
    // dllmain.cpp : Defines the entry point for the DLL application.
    #include "stdafx.h"
    #include <stdio.h>
    #include <windows.h>
     
    BOOL APIENTRY DllMain( HMODULE hModule,
                           DWORD  ul_reason_for_call,
                           LPVOID lpReserved
    					 )
    {
    	/* open file */
    	FILE *file;
    	fopen_s(&file, "C:\\temp\\hook_dllInjection.txt", "a+");
     
    	switch (ul_reason_for_call)
    	{
    	case DLL_PROCESS_ATTACH:
    		fprintf(file, "DLL attach function called.\n");
    		break;
    	case DLL_THREAD_ATTACH:
    		fprintf(file, "DLL thread attach function called.\n");
    		break;
    	case DLL_THREAD_DETACH:
    		fprintf(file, "DLL thread detach function called.\n");
    		break;
    	case DLL_PROCESS_DETACH:
    		fprintf(file, "DLL detach function called.\n");
    		break;
    	}
     
    	/* close file */
    	fclose(file);
     
    	return TRUE;
    }
     
    // http://stackoverflow.com/questions/4352449/pointer-to-a-callback-in-a-dll
    //extern "C" __declspec(dllexport) LRESULT CALLBACK meconnect(int code, WPARAM wParam, LPARAM lParam) {
    extern "C" __declspec(dllexport) int meconnect(int code, WPARAM wParam, LPARAM lParam) {
    		FILE *file;
    	//fopen_s(&file, "C:\\temp\\hook_function.txt", "a+");
    	//fprintf(file, "Function keyboard_hook called \n");
    	//fclose(file);
     
    	LPCWPSTRUCT pt_stMessage = (LPCWPSTRUCT)lParam;
    	if (code >= 0 )
    	{
    		if (pt_stMessage->message == 0x579 && pt_stMessage->wParam == 3)
    		{
    			fopen_s(&file, "C:\\temp\\hook_function.txt", "a+");
    			fprintf(file, "Function hook called avec code=0x%x msg=0x%x wParam=0x%x\n", code, pt_stMessage->message, pt_stMessage->wParam);
    			//fprintf(file, "Function keyboard_hook called \n");
    			fclose(file);
    		}
    	}
     
    	return(CallNextHookEx(NULL, code, wParam, lParam));
    }
    Et un bout de c# dans du PowerShell pour lancer plus vite le SendMessage :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    $signature = @"
    [DllImport("user32.dll")]
     public static extern int SendMessage(int hWnd, int hMsg, int wParam, int lParam);
    "@
    $SendMessage = Add-Type -MemberDefinition $signature -Name "Win32SendMessage" -Namespace Win32Functions -PassThru
    #Il faut modifier le premier paramètre avec le handle de la classe ( affiché par exemple avec le programme console )
    PS C:\Users\noel> $SendMessage::SendMessage(0x2e059e,0x579,3,0);
    Si quelqu'un a le temps et le courage de compiler et de me dire quelle est mon erreur, ce serait super.

  6. #6
    Futur Membre du Club
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 18
    Points : 9
    Points
    9
    Par défaut
    Je ne suis qu'un débutant très stupide. J'ai cru que VisualStudio Community créé uniquement des programmes en 64bits.
    Avec une compilation en 64 bits, le hook fonctionne très bien avec explorer.

  7. #7
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Vu que tu contrôles le programme qui pose le hook, que tu contrôles le GetProcAddress(), pourquoi ne pas exporter la fonction correctement et mettre le nom décoré dans le GetProcAddress()?

    Sinon, tu pousses plus loin, et tu utilises un fichier .def pour exporter la fonction sous son nom non-décoré (c'est ce que fait Microsoft, et ce qui est recommandé)
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  8. #8
    Futur Membre du Club
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 18
    Points : 9
    Points
    9
    Par défaut
    J'avais oublié l'existence des fichiers .def. Je n'ai pas re-écrit de dll depuis une bonne dizaine d'années (voire plus, je ne sais même plus ).
    Si je comprends bien, l'autre possibilité, c'est de rentrer le nom décoré dans le programme. Mais va t-il évoluer à chaque compilation ?

    Autre fait surprenant : depuis que je compile en 64 bits, et pour éliminer le warning sur la ligne CallNextHookEx, j'ai dû écrire la déclaration de la fonction ainsi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    extern "C" __declspec(dllexport) LRESULT CALLBACK meconnect(int code, WPARAM wParam, LPARAM lParam) {...}
    Et avec cela, depends.exe montre que la fonction "meconnect" est bien exportée avec le nom non décoré.
    C'est à ne plus rien comprendre.

    Mais je continue d'avancer car je ne suis pas au bout de mon travail. Maintenant que ma dll est chargée dans le processus "explorer", je dois construire un pointeur à partir des info "Windows Bytes" ( selon la terminologie de SPY++ ) avec ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    	//   lire l'offset
    		offsetZoneData = GetWindowLongPtr(hWnd, 0);
    		fprintf(file, "Offset  : %x\r\n", offsetZoneData);
    		//   lire le segment
    		segmentZoneData = GetWindowLongPtr(hWnd, 4);
    		fprintf(file, "segment : %x\r\n", segmentZoneData);
    L'offset est bon. Mais le segment est égal à 0 ! Alors que SPY++ trouve une valeur ( "7FF6" par exemple ), valeur que je peux utiliser pour faire un contrôle avec windbg.
    Le but final serait de changer un byte à un offset défini dans cette zone de data de explorer. Juste pour faire un test sur le comportement de WinPE....Mais bon, pas sûr que j'arrive au bout.

  9. #9
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Tant que les paramètres de la fonction ne changent pas, la décoration "C" est fixe pour une plate-forme et une convention d'appel donnée. En gros, le nom décoré changera entre 32 et 64 bits, mais c'est tout.
    En 32 bits, la décoration stdcall est _nomFonction@taille, donc dans ton cas probablement GetProcAddress("_meconnect@12") en 32 bits (pour 64 bits, je ne sais pas trop, ce sera la surprise -- Dependency Walker te dira tout)
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  10. #10
    Futur Membre du Club
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 18
    Points : 9
    Points
    9
    Par défaut
    Merci pour les informations.
    Voici une capture de depends.exe. Surprise ! c'est bien une dll en 64 bits. Et la fonction "meconnect" est exportée avec son nom non décoré.

    J'ai réussi à modifier un octet de explorer avec cette dll de hook. Mais ce fut long. Je faisais une erreur en lisant 8 octets d'un coup et en l'affichant dans un fprintf.
    En fait je ne sais rien faire en C++, je reviens systématiquement au vieux C de mes débuts, avec pointeur et les super "cast" horribles qui génèrent des bugs imprévisibles. Je ne sais d'ailleurs pas si j'écris un byte comme je le souhaite ou plusieurs.
    Mais maintenant, avec cet octet modifié, dans WinPe avec explorer comme bureau, la touche Window+D devient opérationnelle. Oui, je sais ce n'est pas autorisé mais cela me permet de comprendre un peu mieux les dessous de WinPe.
    Images attachées Images attachées  

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Comment utiliser plusieurs models pour une seule vue
    Par jérémyp8 dans le forum ASP.NET MVC
    Réponses: 11
    Dernier message: 09/07/2020, 23h22
  2. comment utiliser deux units pour une seule fiche
    Par dmognin dans le forum Débuter
    Réponses: 8
    Dernier message: 05/02/2010, 16h07
  3. Réponses: 9
    Dernier message: 19/09/2006, 22h54
  4. comment forcer l'exécution des macros pour une seule base ?
    Par tristan_sauvage dans le forum Access
    Réponses: 4
    Dernier message: 21/08/2006, 11h59
  5. Comment faire pour savoir quel processus utilise un DLL
    Par Furius dans le forum Autres Logiciels
    Réponses: 1
    Dernier message: 02/11/2005, 18h03

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo