Est-il possible d'exécuter du code dans l'espace de tas?


Réponses:


5

Peut être. Si le tas est exécutable, vous pouvez vous connecter à ce code. Mais certaines variantes Unix rendent l' espace de tas non exécutable , afin de rendre plus difficiles les exploits de certaines vulnérabilités de sécurité telles que les dépassements de tampon (même si vous pouvez injecter du code dans un programme, vous ne pourrez peut-être pas vous y connecter). (Voir l'article lié pour une discussion sur les variantes Unix et leur configuration.) De plus, certaines architectures de processeur ont des caches séparés pour le code et les données , vous devrez donc peut-être émettre une instruction de vidage du cache. Dans l'ensemble, ce n'est pas quelque chose que vous voulez faire à la main.

Il existe une API Unix standard pour charger et exécuter le code, qui fera ce qu'il faut pour rendre le code chargé exécutable: dlopen . Le code doit être chargé à partir d'un fichier.

Les compilateurs juste à temps essaient généralement de trouver des interfaces plus rapides que dlopen. Ils doivent gérer les moyens hautement dépendants de la plate-forme d'assurer l'exécutabilité du code.

EDIT: Merci à Bruce Ediger de m'avoir rappelé la nécessité de vider le cache.


4

Sur certains matériels (comme les processeurs HP-PA de HP), c'est beaucoup plus difficile, et sur d'autres (comme les CPU DEC Alpha), vous devez d'abord effectuer un vidage du cache d'instructions, mais oui, en général, vous pouvez exécuter du code sur le tas. Ce qui suit est un programme en langage C raisonnablement décent qui exécute le code "sur le tas".

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>

/* $Id: cpup4.c,v 1.2 1999/02/25 05:12:53 bediger Exp bediger $ */

typedef int (*fxptr)(
                int, int, int (*)(const char *, ...),
                void *,
                void *(*)(void *, const void *, size_t),
                void *(*)(size_t),
                void (*)(void *),
                size_t
);

char *signal_string(int sig);
void  signal_handler(int sig);
int   main(int ac, char **av);
int copyup(
                int i,
                int j,
                int (*xptr)(const char *, ...),
                void *yptr,
                void *(*bptr)(void *, const void *, size_t),
                void *(*mptr)(size_t),
                void (*ffptr)(void *),
                size_t  size
);
void f2(void);

/* return a string for the most common signals this program
 * will generate.  Probably could replace this with strerror()
 */
char *
signal_string(sig)
                int sig;
{
                char *bpr = "Don't know what signal";

                switch (sig)
                {
                case SIGILL:
                                bpr = "Illegal instruction";
                                break;
                case SIGSEGV:
                                bpr = "Segmentation violation";
                                break;
                case SIGBUS:
                                bpr = "Bus error";
                                break;
                }

                return bpr;
}

/* Use of fprintf() seems sketchy.  I think that signal_handler() doesn't
 * need special compiler treatment like generating Position Independent
 * Code.  It stays in one place, and the kernel knows that place.
 */
void
signal_handler(int sig)
{
                (void)fprintf(
                                stderr,
                                "%s: sig = 0x%x\n",
                                signal_string(sig),
                                sig
                );

                exit(99);
}

int
main(int ac, char **av)
{
                int i, j;

                /* check to see if cmd line has a number on it */
                if (ac < 2) {
                                printf("not enough arguments\n");
                                exit(99);
                }
                /* install 3 different signal handlers - avoid core dumps */
                if (-1 == (i = (long)signal(SIGSEGV, signal_handler)))
                {
                                perror("Installing SIGSEGV signal failed");
                                exit(33);
                }
                if (-1 == (i = (long)signal(SIGILL, signal_handler)))
                {
                                perror("Installing SIGILL signal handler failed");
                                exit(33);
                }
                if (-1 == (i = (long)signal(SIGBUS, signal_handler)))
                {
                                perror("Installing SIGBUS signal handler failed");
                                exit(33);
                }

                setbuf(stdout, NULL);

                /*
                 * print out addresses of original functions so there is something to
                 * reference during recursive function copying and calling 
                 */
                printf(
           "main = %p, copyup %p, memcpy %p, malloc %p, printf %p, free %p, size %ld\n",
           main, copyup, memcpy, malloc, printf, free, (size_t)f2 - (size_t)copyup);

                if ((i = atoi(*(av + 1))) < 1) {
                                printf(" i = %d, i must be > 1\n", i);
                                exit(99);
                }

                printf(" going for %d recursions\n", i);

                j = copyup(1, i, printf, copyup, memcpy, malloc, free, (size_t)f2 - (size_t)copyup);

                printf("copyup at %p returned %d\n", copyup, j);


                return 1;
}

int
copyup(
                int i, int j,
                int   (*xptr)(const char *, ...),
                void *yptr,
                void *(*bptr)(void *, const void*, size_t),
                void *(*mptr)(size_t),
                void  (*ffptr)(void *),
                size_t   size
)
{
                fxptr fptr;
                int k;

                if (i == j)
                {
                                (*xptr)("function at %p got to %d'th copy\n", yptr, i);
                                return i;
                } else
                                (*xptr)("function at %p, i = %d, j = %d\n", yptr, i, j);

                if (!(fptr = (fxptr)(*mptr)(size)))
                {
                                (*xptr)("ran out of memory allocating new function\n");
                                return -1;
                }

                (*bptr)(fptr, yptr, size);

                k = (*fptr)(i + 1, j, xptr, (void *)fptr, bptr, mptr, ffptr, size);

                (*xptr)("function at %p got %d back from function at %p\n",
                                yptr, k, fptr);

                (*ffptr)(fptr);

                return (k + 1);
}

void f2(void) {return;}

1
j'ai essayé cela, mais je ne comprends pas pourquoi j'obtiens un défaut de segmentation pour toutes les valeurs autres que 1.
Sen

Un argument de 1 exécute simplement la fonction - bien que la fonction crée une copie d'elle-même sur le tas, elle n'exécute pas la copie de tas. Je ne sais pas pourquoi vous obtenez une violation de segmentation. Pouvez-vous donner plus de détails sur le compilateur, le processeur, le système d'exploitation, etc., etc.?
Bruce Ediger
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.