Pourquoi transtypez-vous «met externe» en un pointeur de fonction «(void (*) (char *)) & met»?

Je regarde l’exemple abo3.c de Insecure Programming et je ne me plains pas de la dissortingbution dans l’exemple ci-dessous. Quelqu’un pourrait-il m’éclairer?

int main(int argv,char **argc) { extern system,puts; void (*fn)(char*)=(void(*)(char*))&system; char buf[256]; fn=(void(*)(char*))&puts; strcpy(buf,argc[1]); fn(argc[2]); exit(1); } 

Alors, quel est le casting pour le système et les met? Ils retournent tous les deux un int alors pourquoi le jeter à néant?

J’apprécierais vraiment une explication de l’ensemble du programme pour le mettre en perspective.

[MODIFIER]
Merci à vous deux pour votre consortingbution!

Jonathan Leffler , il y a en fait une raison pour que le code soit «mauvais». Il est supposé être exploitable, avec des tampons débordants, des pointeurs de fonction, etc. Une grande partie est encore au-dessus de ma tête.

bta , je déduis de l’article de blog ci-dessus que le système de casting empêcherait en quelque sorte l’éditeur de liens de le supprimer.

Une chose qui n’est pas immédiatement claire, c’est que le système et les adresses sont toutes deux écrites au même endroit. Je pense que c’est peut-être ce dont le gera parle «pour que l’éditeur de liens ne le supprime pas».

Pendant que nous parlons de pointeurs de fonction, je voudrais poser une question complémentaire maintenant que la syntaxe est plus claire. Je regardais des exemples plus avancés utilisant des pointeurs de fonction et suis tombé sur cette abomination, tirée d’un site hébergeant un shellcode.

 #include 

 char shellcode [] = "un shellcode";

 int main (void)
 {
     fprintf (stdout, "Longueur:% d \ n", strlen (shellcode));
     (* (void (*) ()) shellcode) ();
 }

Ainsi, le tableau est converti en une fonction renvoyant void , référencée et appelée? Cela a l’air méchant – alors, à quoi sert le code ci-dessus?

[/MODIFIER]

Question originale

L’utilisateur bta a donné une explication correcte de la dissortingbution – et a commenté sur le caractère peu convaincant du system de diffusion.

Je vais append:

La ligne extern est au mieux étrange. Il est erroné sous ssortingcte C99 car il n’ya pas de type, ce qui le rend invalide; sous C89, le type sera supposé être int . La ligne indique “il existe un entier défini extérieurement appelé système, et un autre appelé”, ce qui est incorrect – il existe une paire de fonctions portant ces noms. Le code peut en réalité “fonctionner” car l’éditeur de liens peut associer les fonctions aux entiers supposés. Mais ce n’est pas sûr pour une machine 64 bits où les pointeurs n’ont pas la même taille que int . Bien entendu, le code doit inclure les en-têtes appropriés ( pour puts() et pour system() et exit() , et pour strcpy() ).

La exit(1); est mauvais à deux égards.

  • Cela indique un échec – inconditionnellement. Vous quittez avec 0 ou EXIT_SUCCESS pour indiquer le succès.

  • À mon avis, il vaut mieux utiliser return à la fin de main() que exit() . Tout le monde n’est pas nécessairement d’accord avec moi, mais je n’aime pas voir exit() comme dernière ligne de main() . La seule excuse à cela est d’éviter les problèmes d’autres mauvaises pratiques, telles que les fonctions enregistrées avec atexit() qui dépendent de l’existence continue de variables locales définies dans main() .


 /usr/bin/gcc -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wssortingct-prototypes -Wold-style-definition -c nasty.c nasty.c: In function 'main': nasty.c:3: warning: type defaults to 'int' in declaration of 'system' nasty.c:3: warning: type defaults to 'int' in declaration of 'puts' nasty.c:3: warning: built-in function 'puts' declared as non-function nasty.c:8: warning: implicit declaration of function 'strcpy' nasty.c:8: warning: incompatible implicit declaration of built-in function 'strcpy' nasty.c:10: warning: implicit declaration of function 'exit' nasty.c:10: warning: incompatible implicit declaration of built-in function 'exit' nasty.c: At top level: nasty.c:1: warning: unused parameter 'argv' 

Pas bon code! Je m’inquiète pour une source d’informations contenant un tel code et n’expliquant pas toute cette horreur (parce que la seule excuse pour afficher un code aussi complexe est de le disséquer et de le corriger).


Il y a une autre bizarrerie dans le code:

 int main(int argv,char **argc) 

C’est «correct» (cela fonctionnera) mais 100% conventionnel. La déclaration normale est:

 int main(int argc, char **argv) 

Les noms sont des abréviations pour ‘nombre d’arguments’ et ‘vecteur d’arguments’, et utiliser argc comme nom du vecteur (tableau) de chaînes est anormal et déroutant.


Après avoir visité le site référencé, vous pouvez voir qu’il passe par une série d’exemples gradués. Je ne suis pas sûr si l’auteur a simplement un angle mort sur la question argc / argv ou s’il délibérément déconne ( «abo1» suggère qu’il joue, mais à mon avis, cela n’est pas utile). Les exemples sont supposés nourrir votre esprit, mais leur explication n’est pas très explicite. Je ne pense pas que je pourrais recommander le site.


Question d’extension

Que font les acteurs dans ce code?

 #include  char shellcode[] = "some shellcode"; int main(void) { fprintf(stdout,"Length: %d\n",strlen(shellcode)); (*(void(*)()) shellcode)(); } 

Cela prend l’adresse de la chaîne ‘shellcode’ et la traite comme un pointeur sur une fonction qui prend un ensemble d’arguments indéterminé, ne renvoie aucune valeur et l’exécute sans argument. La chaîne contient le code assembleur binary de certains exploits – exécutant généralement le shell – et le but de l’intrus est d’obtenir un programme à privilèges root pour exécuter son shellcode et de leur donner une invite de commande, avec les privilèges root. À partir de là, le système leur appartient. Pour la pratique, la première étape consiste à obtenir un programme non-root pour exécuter le shellcode, bien sûr.

Revoir l’parsing

L’parsing sur le site Web de Mishou ne fait pas autant autorité que j’aimerais:

Premièrement, ce code utilise le mot-clé extern en langage C pour rendre le système et met les fonctions disponibles. Ce que cela fait (je pense) fait essentiellement référence directement à l’emplacement d’une fonction définie dans les fichiers d’entête (implicites)… j’ai l’impression que GDB inclut automatiquement les fichiers d’entête stdlib.h pour system et stdio.h pour les options de vente. . Une chose qui n’est pas immédiatement claire, c’est que le système et les adresses sont toutes deux écrites au même endroit. Je pense que c’est peut-être ce dont le gera parle «pour que l’éditeur de liens ne le supprime pas».

Disséquer le commentaire:

  1. La première phrase n’est pas très précise. il indique au compilateur que le system symboles et d’ puts est défini (sous forme d’entiers) ailleurs. Lorsque le code est lié, l’adresse de la fonction puts() est connue; le code la traitera comme une variable entière, mais l’adresse de la variable entière est, en fait, l’adresse de la fonction. Le transtock force donc le compilateur à la traiter comme un pointeur de fonction.
  2. La deuxième phrase n’est pas tout à fait exacte; l’éditeur de liens résout les adresses des «variables» externes via les symboles de fonction system() et puts() dans la bibliothèque C.
  3. GDB n’a absolument rien à faire pour la compilation ou la création de liens.
  4. La dernière phrase n’a aucun sens. Les adresses ne sont écrites qu’au même emplacement, car vous disposez d’une initialisation et d’une affectation à la même variable.

Cela ne m’a pas motivé à lire l’article en entier, il faut le dire. La diligence raisonnable me force à avancer; L’explication suivante est meilleure, même si elle n’est pas aussi claire que je le pense. Mais l’opération consistant à faire déborder le tampon avec une chaîne d’arguments trop longue mais soigneusement conçue constitue le cœur de l’opération. Le code mentionne à la fois les dlopen() puts() et system() Ainsi, lorsqu’elles sont exécutées en mode non-exploit, la fonction dlopen() est un symbole connu (sinon, vous devrez utiliser dlopen() pour trouver son adresse), et ainsi de suite. que lorsqu’il est exécuté en mode exploit, le code dispose du system() symboles system() pour une utilisation directe. Les références externes non utilisées ne sont pas mises à disposition dans l’exécutable – ce qui est une bonne chose lorsque vous vous rendez compte du nombre de symboles contenus dans un en-tête système typique par rapport au nombre utilisé par un programme incluant l’en-tête.

Quelques astuces sont présentées – bien que leur mise en œuvre ne soit pas indiquée sur la page spécifique; Je suppose (sans l’avoir vérifié) que les informations pour le programme getenvaddr sont disponibles.

Le code abo3.c peut s’écrire comme suit:

 #include  #include  #include  int main(int argc, char **argv) { void (*fn)(char*) = (void(*)(char*))system; char buf[256]; fn = (void(*)(char*))puts; strcpy(buf, argv[1]); fn(argv[2]); exit(1); } 

Maintenant, il comstack avec un seul avertissement avec les options de compilation difficiles que j’ai utilisées à l’origine – et c’est l’avertissement précis que “argc” n’est pas utilisé. C’est tout aussi exploitable que l’original; c’est un “meilleur” code car il comstack proprement. Les indirections étaient une mystique inutile, pas un élément crucial pour rendre le code exploitable.

Les deux system et puts normalement revenir int . Le code les convertit en un pointeur qui renvoie void , probablement parce qu’ils veulent ignorer la valeur renvoyée. Cela devrait être équivalent à utiliser (void)fn(argc[2]); comme dernière ligne si la dissortingbution n’a pas changé le type de retour. La suppression du type de retour est parfois effectuée pour les fonctions de rappel, et cet extrait de code semble être un exemple simpliste de rappel.

Pourquoi la dissortingbution pour le system si elle n’est jamais utilisée me dépasse. Je suppose qu’il y a plus de code qui n’est pas affiché ici.