C++ Débordement De La Mémoire Tampon
Je suis en train de me former sur les dépassements de mémoire tampon et d'exploitation en C++. Je suis un intermédiaire en C++ mec, au mieux, si patient avec moi. J'ai suivi quelques tutos, mais voici un exemple de code pour illustrer ma question:
#include <string>
#include <iostream>
using namespace std;
int main()
{
begin:
int authentication = 0;
char cUsername[10], cPassword[10];
char cUser[10], cPass[10];
cout << "Username: ";
cin >> cUser;
cout << "Pass: ";
cin >> cPass;
strcpy(cUsername, cUser);
strcpy(cPassword, cPass);
if(strcmp(cUsername, "admin") == 0 && strcmp(cPassword, "adminpass") == 0)
{
authentication = 1;
}
if(authentication)
{
cout << "Access granted\n";
cout << (char)authentication;
}
else
{
cout << "Wrong username and password\n";
}
system("pause");
goto begin;
}
Je sais qu'il y a toutes sortes de mauvais juju ici avec cin << String
, etc... de toute façon, quand j'entre dans un trop grand nombre de lettres (une tonne de A
's par exemple) dans cUser
et cPass
, je viens d'obtenir une Violation d'Accès à partir de Visual Studio. Si, toutefois, j'type 20ish A
's, puis un espace, puis un autre A
en cUser
, il saute de me demander de cPass
(en supposant que parce qu'il a été rempli après le caractère espace causé l'appel précédent de cin
de retour) et il suffit de m'en donne l'accès.
À ce point, et pourquoi, sont des données qui débordent dans "authentification" et pourquoi ça ne se passe lorsque je avoir de l'espace et non pas quand j'ai un million de A
s '... je n'ai jamais eu la "Violation d'Accès" lorsque j'utilise un espace dans l'entrée pour cUser
.
Êtes-vous essayer d'obtenir le débordement et vous demandez-vous pourquoi il fait ça? Ou voulez-vous pas de débordement et demandent une solution? Aussi, vous ne devriez pas le système(). C'est une énorme porte dérobée pour un pirate.
Aussi, j'ai un programme appelé
pause
sur mon système qui lance des missiles nucléaires. Donc, je ne devrais pas exécuter votre programme.pourquoi utilisez-vous le char arrarys lorsque vous avez inclus la chaîne d'en-tête ??? aussi goto et system("pause")... je ne peux pas punir votre auto plus que vous l'avez fait.
system("command")
est requis par la norme et hérité par le C++, sur toutes les plateformes. Mais son comportement est complètement plate-forme spécifique. Sur certaines plates-formes, un hacker pourrait installer un fichier qui serait jugée et exécutée à la place de celui prévu.
OriginalL'auteur sraboy | 2012-01-09
Vous devez vous connecter pour publier un commentaire.
J'ai modifié votre programme un peu pour le rendre plus illustratif:
J'ai compilé avec x64 compilateur de ligne de commande MS compilateur, pas d'optimisations. Alors maintenant, nous avons un exe que nous voulons "hack". On charge le programme avec WinDbg (vraiment bon débogueur) et de prendre un coup d'oeil lors du démontage (remarquez, j'ai fourni complet des informations de débogage, pour plus de clarté):
Maintenant, puisque nous savons comment x64 pile fonctionne, nous pouvons commencer le "piratage".
RSP
est le pointeur de pile, pile de fonction est adresses ci-dessusRSP
valeur (pile grandit vers les petites adresses). Ainsi, nous voyons queRSP+28h
est oùcUsername
,RSP+38h
estauthentication
, etRSP+40h
estcPassword
, où 28h, 38h et 40h sont hexadécimal décalages. Voici petite image pour illustrer:Que voyons-nous partir d'ici? Nous voyons que le compilateur de données alignées sur 8 octets limites: par exemple, nous avons demandé à allouer 10 octets pour
cUsername
, mais nous avons eu 16 octets - x64 bits pile est alignée sur la limite de 8 octets, naturellement. Cela signifie que pour écrire dansauthentication
nous avons besoin d'écrire danscUsername
PLUS que 16 octets (symboles). Notez également, que le compilateur mettrecPassword
plus élevé queauthentication
- nous ne pouvons pas remplacerauthentication
à l'aide decPassword
, seulementcUsername
.Alors maintenant, nous avons un programme et entrée
Username: 0123456789abcdef1
.0123456789abcdef
= 16 octets, la prochaine1
va être mis en octet de poids faible deauthentication
- assez bon pour nous:Si vous êtes intéressé par la façon dont le compilateur génère du code, disposition de la mémoire, etc, alors que peu de programmes de test comme la vôtre sont la voie à suivre. Viens de lire un peu plus sur la plupart des communes des instructions en assembleur et de la pile mise en page pour votre PROCESSEUR, et d'obtenir un débogueur qui vous permettra de démonter. Ensuite, vous serez en mesure de voir comment votre code C/C++ est transformé en véritable machine des instructions. Il permet non seulement d'utiliser certains "trucs", mais vous donne également l'idée, par exemple, à propos de ce que les choses compilateur est capable d'optimiser etc.
Salut, je suis novice en c++. J'ai une question idiote à poser, comment pouvons-nous limiter l'entrée de l'utilisateur à la variable de taille que nous avons déclaré? Par exemple, cUsername accepte seulement 10 caractères, mais l'utilisateur peut entrer plus de ce que le droit? Il est possible d'éviter le débordement de cette variable?
Oui. Par exemple, vous pouvez utiliser
std::setw()
:std::cin >> std::setw(9) >> cUsername;
.De l'oci, je vous remercie beaucoup.
OriginalL'auteur lapk
Il est d'écraser votre
authentication
variable. Cela signifie queauthentication
est positif avant même que votre code vérifie le nom d'utilisateur et mot de passe. Pour le vérifier, imprimer authentification avant les contrôles.Je vais développer un peu plus loin: Lorsque vous tapez est un très long nom d'utilisateur, nom d'utilisateur est copié, par votre
strcpy
, encUsername
. Cette variablecUsername
est immédiatement aprèsauthentication
et par conséquent, il est remplacé par le trop-long nom d'utilisateur.Si vous tapez un très très long nom d'utilisateur, puis (encore une fois) l'authentification variable sera remplacée. Mais aussi maintenant, les éléments le plus haut dans la pile, tels que la valeur de retour, sera remplacé. Si votre programme écrase trop haut de la pile, puis elle sera très mal cassé et tout peut arriver. Vous essentiellement d'exécuter du code aléatoire à ce point.
Je ne pense pas que l'espace est pertinent. Tous les espaces sont traités de la même façon par
cin
. Je pense que votre question est "Quand j'ai environ 20 caractères dans le nom d'utilisateur, le programme procède à l'authentification, et quand j'ai beaucoup plus de caractères (par exemple 100), puis-je obtenir une violation d'accès. Pourquoi cette différence?"Alrighty, alors. Merci pour la réponse... étant autodidacte, je peux écrire du code à partir d'ici au royaume-venir, mais je ne peux pas debug pour ma vie et ne savent pas beaucoup au sujet de la pile et l'allocation de mémoire. Apprécie l'info.
le compilateur tu utilises? J'ai fait des expériences et g++ et clang. Lorsque vous utilisez clang? La version de compilateur?
J'ai juste utilisé Visual Studio 2010, de sorte que MSVC++... selon le compilateur, je suis maintenant, pas sûr de ce que les mises à jour du/des service packs que j'ai à ce point. De manière générale, j'juste aller avec C# ou VB.NET/GDI pour mes applications, de sorte que cette C++ trucs avec la gestion de la mémoire, etc est assez nouveau pour moi. Je suis habitué à tous les code managé.
OriginalL'auteur Aaron McDaid
Si vous utilisez
std::string
, vous trouverez que votre programme sera beaucoup plus simple:Edit:
En ce qui concerne votre dernière question, je pense que par défaut,
cin >> string
arrêter de lire le premier caractère d'espacement (c'est à dire de l'espace), donc si vous saisissez un espace,cin
va s'arrêter avant qu'il corrompt les données, et si vous n'obtenez pas la violation d'accès. Si vous voulez être en mesure de lire des espaces, puis vous aurez besoin d'utilisergetline
comme j'ai ci-dessus de sorte qu'il va lire l'intégralité de la ligne de texte, espaces inclus.Ah! Merci. Je n'avais pas réalisé que. Donc, cin simplement cesse de lecture à l'espace et cela l'empêche de lecture trop en mémoire lorsque j'essaie de cin >> uPass. Je savais qu'il s'arrêtait dans les espaces, mais n'a pas mis 2 et 2, ainsi que sur un certain nombre de choses. Cours d'exécution à travers avec le débogueur, je peux voir ce qui se passe maintenant. Merci!
OriginalL'auteur dreamlax
Solution proposée pour détecter pointeur NULL et de dépassement de tampon dans memcpy, memset, strcpy avant de la main et imprimer l'emplacement (fichier:ligne) où le problème se produit:
OriginalL'auteur Danh Hoang
Lorsque vous compilez un programme, le compilateur décide de la façon d'organiser vos données dans la mémoire. Si un programme contient pas cochée accès à des tableaux, il peut être exploitable comme un utilisateur malveillant avec la connaissance de l'organisation des données en mémoire peuvent comprendre comment remplacer les variables critiques.
Cependant, le C++ n'est pas de vous donner un contrôle complet sur la façon dont les choses sont mises sur la pile. Les variables locales peuvent apparaître dans n'importe quel ordre dans la mémoire.
À comprendre débordement de la mémoire tampon exploits, vous devrez démonter votre programme et de se plonger dans le code machine. Cela vous donnera la disposition de la pile, y compris l'importance des adresses de retour.
Par le chemin, la "Violation d'Accès" est à venir à partir de votre programme, pas de Visual Studio. Vous avez probablement besoin de plus d'expérience avec "l'avant" de l'ingénierie avant d'entrer dans l'ingénierie inverse.
Je vais être sûr de travailler sur mon avant ingénierie... apprécier le trait d'esprit. J'essaie de comprendre pourquoi j'obtiens une "Violation d'Accès" dans un cas et pas dans l'autre (avec l'espace).
Pas conçu comme un trait d'esprit ou un jab... de toute façon les résultats sont imprévisibles, car vous ne démarre pas avec suffisamment d'informations pour faire une prédiction. Vous êtes à la prise de vue dans l'obscurité, attendez-vous à obtenir des résultats bien définis?
Je ne savais pas à quoi m'attendre. C'était tout nouveau pour moi. Votre réponse n'a pas vraiment l'expliquer. J'avais une question d'ordre pratique; votre réponse était abstrait. Combiné avec AzzA, ça aide, surtout en sachant que votre référence à "variables locales peuvent apparaître dans n'importe quel ordre" est mieux expliquée par lui, m'informant que le compilateur a tout simplement choisi de placer cPassword au-dessus de l'authentification. Je ne sais pas beaucoup au sujet de la pile et il suffit de programmer jusqu'à ce qu'il fonctionne. Ceci est ma première tentative de comprendre ce genre de choses d'un point de vue pratique. Je suis une .NET (et QBASIC) guy, de sorte que la gestion de la mémoire, etc est nouveau pour moi.
OriginalL'auteur Potatoswatter
Parce que vous avez votre jeu de caractères à 10 places (y compris le
NULL
caractère), quelque chose de plus long va déborder surAuthentication
. Il existe de nombreuses façons de résoudre ce problème, le plus évident étant tout simplement faire le char plus gros. D'autres moyens serait de limiter le nombre de lettres d'un utilisateur entre à l'inscription (en supposant que c'était sur un serveur de site web). Vous pouvez également utiliserstrlen(cUsername)
à compter de la longueur du char tableau et demander de re-entrer le nom d'utilisateur avec moins de personnages.EDIT:
Ok. Donc, ce que vous voulez faire est d'utiliser
getline(cin,cUser)
à la place.cin
arrêt de la lecture à la première occurence d'un espace.getline()
permettra de lire l'ensemble de la chaîne, avec ou sans espace.À l'aide de
strlen(cUsername)
pour vérifier si l'utilisateur a saisi un trop grand nombre de caractères ne fonctionne pas alors parce qu'il est trop tard. Vous voulez arrêter de lire quandcUsername
est plein, de ne pas vérifier si il est trop plein par la suite.OriginalL'auteur