VirtualProtect qui crash

Signaler
Messages postés
17
Date d'inscription
mercredi 10 juin 2009
Statut
Membre
Dernière intervention
3 juillet 2009
-
Messages postés
17
Date d'inscription
mercredi 10 juin 2009
Statut
Membre
Dernière intervention
3 juillet 2009
-
Bonjour, après avoir lu plusieurs sources de ce site sur le hook de l'API, j'essaye de réaliser ma propre source, pour bien comprendre comment cela fonctionne.

Je réalise donc un hook global, puis lorsque l'application qui m'intéresse map ma DLL, je récupère l'adresse de la fonction qui m'intéresse via les instructions suivantes :

    HMODULE hDll;
    GetModuleHandleEx( 0, dllName, &hDll );
    DWORD *adresse = (DWORD*)GetProcAddress( hDll, fonction );


Peut être qu'il y a un problème à ce niveau?

Puis je veux ensuite modifier cette adresse, donc je doit modifier les accès à cette zone mémoire, via la fonction VirtualProtect :

   MessageBox( 0, L"Ici", L"Debug", 0 );
   VirtualProtect( (LPVOID)adresse, 4, PAGE_EXECUTE_READWRITE, previousProtection );
   MessageBox( 0, L"Ici2", L"Debug", 0 );


J'ai entouré la fonction de MessageBox, pour m'assurer que c'est bien à ce niveau que l'application plante. Donc je n'ai pas d'erreur de compilateur, mais lors de l'exécution, je recois ma première MessageBox, puis l'application cesse de fonctionner.

Ce que je ne comprends pas c'est pourquoi la fonction n'échoue pas tout simplement auquel cas je pourrais récupérer le message d'erreur...

Merci pour votre aide.

20 réponses

Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
13
Salut,

C'est quoi ton previousProtection ? Si c'est un DWORD, c'est mal barré. Il attend un PDWORD.

VirtualProtect( (LPVOID)adresse, 4, PAGE_EXECUTE_READWRITE, &previousProtection );

C'est logique. C'est un paramètre de sortie -> il lui faut l'adresse d'un DWORD.
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
13
Exemple de hook comme tu le fais ici.
Messages postés
17
Date d'inscription
mercredi 10 juin 2009
Statut
Membre
Dernière intervention
3 juillet 2009

D'accord avec toi, j'avais déclaré ainsi:

DWORD *previousProtection = NULL;

Donc normalement c'est bon... Sachant que typedef DWORD *PDWORD
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
13
lol !

Bin non...

A quoi revient ton code ? A ça :

VirtualProtect( (LPVOID)adresse, 4, PAGE_EXECUTE_READWRITE, NULL);

Donc boum !

Tu peux éventuellement faire pointer sur un DWORD si tu tiens à ton pointeur :
DWORD *lpPreviousProtection;
DWORD previousProtection;
lpPreviousProtection = &previousProtection;
Messages postés
17
Date d'inscription
mercredi 10 juin 2009
Statut
Membre
Dernière intervention
3 juillet 2009

En effet je n'avais pas été scrupuleux en initialisant à NULL, je viens de relire la doc.

Cependant la méthode que tu me donnes, à savoir :

DWORD *lpPreviousProtection;
DWORD previousProtection;
lpPreviousProtection = &previousProtection;

Est je pense valide, mais l'application plante toujours au même endroit et de la même façon :s...
Messages postés
17
Date d'inscription
mercredi 10 juin 2009
Statut
Membre
Dernière intervention
3 juillet 2009

J'ai été voir une de tes sources qui traite du sujet, et tu dis qu'une fonction utilisée par l'exe, mais importée par le biais d'une autre DLL, ne peut pas être filtrée?

Celà vient sans doute de là mon problème, puisque l'exe fait appel à une DLL network, qui elle même fait appel à la DLL WS2_32...

Est ce possible que mon problème vienne de là?
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
13
Juste pour être sûr...
Avec mon code :

DWORD *lpPreviousProtection;
DWORD previousProtection;
lpPreviousProtection = &previousProtection;

Faut pas faire l'appel comme ça :
VirtualProtect( (LPVOID)adresse, 4, PAGE_EXECUTE_READWRITE, previousProtection );
mais comme ça :
VirtualProtect( (LPVOID)adresse, 4, PAGE_EXECUTE_READWRITE, lpPreviousProtection );

Est ce possible que mon problème vienne de là?

Non. Avec la méthode que tu utilises ici, ça marche dans tous les cas.
Messages postés
17
Date d'inscription
mercredi 10 juin 2009
Statut
Membre
Dernière intervention
3 juillet 2009

Oui, je ne me suis pas trompé dans l'appel de la fonction...

Mais pourtant la fonction ne devrait pas apparaitre dans l'IAT de l'exe si il ne la charge pas directement?
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
13
Oui, la fonction n'apparaît pas dans la table d'import.

Mais la méthode utilisée ici écrase la fonction elle même. Pas la table d'import.

Au fait, tu as testé "adresse" ? Si la dll n'est pas (encore) chargée, GetModuleHandleEx renvoie rien de bon. Faut tout tester, comme j'ai fait dans mon code :
<hr size="2" width="100%" />  /* Chargement de la librairie */
  hUser32 = LoadLibrary(_T("user32.dll"));
  if (! hUser32) goto the_end;

  /* Récupération de l'adresse de la fonction Win32 */
  lpWin32MsgBox = (char*)GetProcAddress(hUser32, MSGBOX_FUNC);
  if (! lpWin32MsgBox) goto free_lib;

  /* Lock de la zone pour la rendre accessible en écriture */
  if (! VirtualProtect(lpWin32MsgBox, 5, PAGE_EXECUTE_READWRITE, &nOldProtect)) goto free_lib;
Messages postés
17
Date d'inscription
mercredi 10 juin 2009
Statut
Membre
Dernière intervention
3 juillet 2009

Je teste si GetModuleHandleEx ne me renvoit pas NULL. Non je ne teste pas "adresse" si la dll n'est pas chargée, ca changerai quoi?

Quand tu dis écrases la fonction elle même, c'est à quel niveau? Uniquement au niveau du programme concerné?
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
13
Uniquement au niveau du processus concerné.
Messages postés
17
Date d'inscription
mercredi 10 juin 2009
Statut
Membre
Dernière intervention
3 juillet 2009

Je viens de faire un grand pas vers la solution je pense, en effet ce n'est pas la fonction VirtualProtect qui plante, mais la lecture de ma variable!

Voici le code qui m'a permis de le déceller :

    MessageBox( 0, L"Ici", L"Debug", 0 );
    MessageBox( 0, (LPCWSTR)adresse, L"Debug", 0 );
    //VirtualProtect( (LPVOID)adresse, 4, PAGE_EXECUTE_READWRITE, lpPreviousProtection );

La première MessageBox s'affiche, et ça plante de suite après.

Voici d'où me viens cette variable :

    HMODULE hDll;
    GetModuleHandleEx( 0, dllName, &hDll );
    adresse =  (DWORD*)GetProcAddress( hDll, fonction );

Je pense que le fait d'utiliser un pointeur peut causer des problème d'accès, mais pourtant je fais uniquement de la lecture sur le code que je présente, donc je ne comprends pas trop...

Je serai ravi d'avoir l'avis d'un expert sur la question.
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
13
MessageBox( 0, (LPCWSTR)adresse, L"Debug", 0 );

Bon tu me diras, ça peut ne pas planter, vu que MessageBox va s'arrêter au premier octet à zéro, et qu'il y a de bonne chance pour qu'il y en ait un rapidement...

Mais je vois pas ce que ça va t'apporter une MessageBox pleine de signes bizarres !

Tu as testé adresse avec NULL ?
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
13
Tu es sous Code::Blocks/MinGW ?

Il a un débogueur. Tu peux faire du pas à pas et voir le contenu des variables locales facilement.
Aide sur le debugging sous C::B à la fin de ce pdf.
Messages postés
17
Date d'inscription
mercredi 10 juin 2009
Statut
Membre
Dernière intervention
3 juillet 2009

Je code avec VC++ 2008 Express.

La MessageBox ne m'apporte rien comme message lisible en effet, mais juste la preuve que le bug de mon application se produit lorsque je sollicite la variable "adresse". J'ai aussi essayé dans un if, à chaque fois ça plante lorsque j'appelle cette variable.

Cette variable est l'attribut d'une classe, lorsque j'initialise et manipule cette variable dans le constructeur tout vas bien, mais lorsque j'utilise ma variable dans une autre fonction, crash...
Messages postés
17
Date d'inscription
mercredi 10 juin 2009
Statut
Membre
Dernière intervention
3 juillet 2009

Je crois avoir trouvé pourquoi cela ne marche pas!
J'installe mon hook après le process attach sur mon programme, je dois donc être en dehors de l'espace mémoire du programme d'où le plantage lorsque je demande l'adresse de la variable.

Je modifie tout ça et je vous tiens au courant.
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
13
VC++ a (aussi !) un débogueur...

Que :
MessageBox( 0, (LPCWSTR)adresse, L"Debug", 0 );
crashe ne prouve pas une seconde que adresse a une mauvaise valeur.

En effet, cela revient à demander l'affichage des octets situés à l'adresse contenue dans ta variable adresse. Sachant que si tout s'est bien passé, ces octets sont ceux du code d'une fonction ! Donc pas du tout une chaîne. Bilan il peut ne pas y avoir de zéro terminal avant une zone que tu n'as pas le droit de lire (Dans les faits les octets à 0 sont courant dans du code, mais quand même, c'est chercher le bâton pour se faire battre).

Donc ce code peut planter même avec "adresse" contenant ce qui t'intéresse.

Si tu tiens à déboguer à la MessageBox, utilise ce genre de code :
<hr size="2" width="100%" />#include <windows.h>

void DisplayVal(long val)
{
  char lpBuffer[50];

  wsprintf(lpBuffer, "%lX", val);
  MessageBox(0, lpBuffer, NULL, MB_OK);
}

int main()
{
  int i;

  i = 2;
  /* Affichage de l'adresse de i */
  DisplayVal((long)&i);

  /* Afficage de i */
  DisplayVal(i);
  return 0;
}
Messages postés
17
Date d'inscription
mercredi 10 juin 2009
Statut
Membre
Dernière intervention
3 juillet 2009

Merci pour l'astuce avec la MessageBox ça me servira sans doute!

Bon en effet le problème venait bien du fait que je faisais appel à la fonction en dehors de l'espace mémoire du programme en question...

Donc maintenant la fonction fonctionne, mais le processus cible stop net et sans message d'erreur une fois la fonction remplacée.

Voici le code :

    DWORD *lpPreviousProtection;
    DWORD previousProtection;
    lpPreviousProtection = &previousProtection;
    MessageBox( 0, L"Ici", L"Debug", 0 );
    VirtualProtect( (LPVOID)adresse, 4, PAGE_EXECUTE_READWRITE, lpPreviousProtection );
    MessageBox( 0, L"Ici2", L"Debug", 0 );
    if ( !IsBadWritePtr( (LPVOID)a_addrOrigine, 4 ) ) {
        *adresse = *adresse2;
        MessageBox( 0, L"Adresse modifiee", L"Info", 0 );
    }
    MessageBox( 0, L"Ici3", L"Debug", 0 );
    VirtualProtect( (LPVOID)adresse, 4, *lpPreviousProtection, lpPreviousProtection );
    MessageBox( 0, L"Ici4", L"Debug", 0 );

Voilà après l'execution de cette fonction, le processus ferme d'un coup.

J'avoue ne pas avoir trop d'idée sur la nature du problème ...
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
13
Sur quoi pointe adresse2 ?

*adresse = *adresse2;

Tu recopies 4 octets... Je ne sais pas ce que tu essais de faire, mais c'est mal barré.
adresse, ce n'est pas un pointeur sur l'adresse de la fonction.
C'est un pointeur sur la fonction.

Quand la fonction sera appelé, pouf, le processeur exécutera le code situé à adresse.
Donc faut pas mettre une adresse à cet emplacement.
Faut mettre un saut.
jmp MonHook

Sachant que le saut est relatif, donc il faut calculer l'offset entre ton hook et la fonction d'origine.

Le saut plus offset, ça fait 5 octets à écrire :
<hr size="2" width="100%" />  /* Ecriture de l'opcode de jmp */
  lpWin32MsgBox[0] = 0xE9;

  /* Calcul du saut, corrigé car eip pointe l'instruction d'après */
  nOffset = (int)&MyMessageBox - (int)(lpWin32MsgBox + 5);

  /* On passe le E9 */
  lpOffset = (int*)&lpWin32MsgBox[1];

  /* Mise en place de l'offset du saut */
  lpOffset[0] = nOffset;
<hr size="2" width="100%" />IsBadWritePtr -> NE JAMAIS UTILISER CETTE FONCTION !

Tu as lu la doc et les commentaires ?
Messages postés
17
Date d'inscription
mercredi 10 juin 2009
Statut
Membre
Dernière intervention
3 juillet 2009

Non, j'avoue que je n'ai pas travaillé suffisamment sur ton code, les deux codes que j'ai lu et relus dans tout les sens sont les suivants : ( Merci aux auteurs d'ailleurs. )

http://www.cppfrance.com/codes/SNIFFER-MSN-MESSENGER-GRACE-HOOK-FONCTIONS-WINSOCK_32374.aspx

Dans celui là il ne change que 4 octets.
Et le second où effectivement il change 5 octets : ( j'ai d'ailleurs déjà essayé avec un jump, et au lieu que l'application plante instantanément, il plante au bout de 2 secondes environ )

http://www.cppfrance.com/codes/API-HOOKING_30610.aspx

Je vais maintenant décomposer ta source ( merci ) pour essayer de comprendre comment tu t'y prends.