43 votes

"xor eax, ebp" étant utilisé dans la sortie du compilateur C++

Je viens d'essayer de compiler quelques extraits de code C++ sur VS2010 et d'analyser les exécutables sur IDA Pro. Une chose que j'ai remarquée est que la plupart d'entre eux ont quelque chose comme ce qui suit au début (peu de temps après un appel à __security_check_cookie)

xor eax, ebp

et quelque chose comme

xor ecx, ebp

en bas. Pourquoi cela se produit-il? L'optimisation du compilateur était désactivée.

69voto

paxdiablo Points 341644

Il s'agit ici de méthodes de protection contre les dépassements de tampon, et qui n'ont rien à voir avec l'optimisation du compilateur. MSVC va (si vous spécifiez l'/GS switch) placer un cookie de sécurité sur la pile près de l'adresse de retour afin de détecter un cas courant de corruption de pile.

La corruption de pile peut être causée soit par un mauvais code du genre :

char buff[5];
strcpy(buff, "Man, this string is waaay too long!!");

soit par des utilisateurs malveillants exploitant de mauvaises pratiques de codage, comme l'utilisation de scanf("%s", myBuff) pour l'entrée utilisateur. Des attaques soigneusement élaborées comme celle-ci peuvent suborner votre programme pour effectuer des actions que vous ne voulez probablement pas.

En plaçant un cookie près de l'adresse de retour, un grand nombre de bugs (et vecteurs d'attaque) peuvent être empêchés, simplement parce que les corruptions de mémoire tendent à être séquentielles. En d'autres termes, si vous avez remplacé l'adresse de retour, c'est probablement parce que vous avez commencé à écrire d'un côté du cookie et corrompu la mémoire jusqu'à l'adresse de retour de l'autre côté du cookie (d'où le cookie sera également écrasé).

Cela ne détecte pas tous les bugs puisque vous pourriez avoir un code du genre :

char buff[5];
buff[87] = 'x';

qui pourrait potentiellement corrompre l'adresse de retour sans toucher au cookie. Mais cela attrapera tous ceux malveillants qui reposent sur l'entrée d'une chaîne plus longue que prévu, qui corrompent jusqu'à l'adresse de retour (y compris le cookie).

La séquence que vous voyez probablement dans le code ressemble à :

mov  eax, dword ptr ds:___sec_cookie   ; valeur fixe.
xor  eax, ebp                          ; ajuster en fonction du pointeur de base.
mov  [ebp+SOMETHING], eax              ; stocker la valeur ajustée.

qui personnalise le cookie, en fonction du pointeur de base actuel.

Cela changera ce qui est réellement mis sur la pile à chaque niveau de pile (et également en fonction du nombre de paramètres et de leurs tailles) et est probablement une tentative de sécuriser davantage le code contre les intentions malveillantes, en garantissant qu'une signature variable est écrite sur la pile plutôt qu'une valeur fixe (sinon l'attaquant pourrait entrer des caractères incluant un cookie valide).

Et la séquence à la fin sera quelque chose comme ceci :

mov  ecx, [ebp+SOMETHING]              ; obtenir le cookie ajusté.
xor  ecx, ebp                          ; le réajuster, car
                                       ;   ((N xor X) xor X) == N.
call @__sec_check_cookie               ; vérifier le cookie.

C'est essentiellement le processus inverse de celui décrit ci-dessus. L'appel de @__sec_check_cookie ne reviendra que si ecx est réglé sur la bonne valeur du cookie. Sinon il lèvera une erreur, comme confirmé ici:

La routine __security_check_cookie() est simple : si le cookie n'a pas été modifié, elle exécute l'instruction RET et termine l'appel de fonction. Si le cookie ne correspond pas, la routine appelle report_failure().

La fonction report_failure() appelle alors __security_error_handler(). Les deux fonctions sont définies dans le fichier seccook.c des fichiers source du runtime C (CRT).

Le support CRT est nécessaire pour que ces vérifications de sécurité fonctionnent. Lorsqu'une erreur de vérification de sécurité se produit, le contrôle du programme est passé à __security_error_handler(), qui est résumé ici :

void __cdecl __security_error_handler(int code, void *data)
{
    if (user_handler != NULL) {
      __try {
        user_handler(code, data);
      } __except (EXCEPTION_EXECUTE_HANDLER) {}
    } else {
      //...préparer outmsg...

      __crtMessageBoxA(
          outmsg,
          "Microsoft Visual C++ Runtime Library",
          MB_OK|MB_ICONHAND|MB_SETFOREGROUND|MB_TASKMODAL);
    }
    _exit(3);
}

Par défaut, une application qui échoue à une vérification de sécurité affiche une boîte de dialogue indiquant "Détection de dépassement de tampon !". Lorsque la boîte de dialogue est dismissée, l'application se termine.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X