Ist es möglich, Code im Heap-Bereich auszuführen?


Antworten:


5

Könnte sein. Wenn der Heap ausführbar ist, können Sie zu diesem Code verzweigen. Einige Unix-Varianten machen den Heap-Speicher jedoch nicht ausführbar , um das Ausnutzen einiger Sicherheitslücken wie Pufferüberläufe zu erschweren (selbst wenn Sie Code in ein Programm einfügen können, können Sie möglicherweise nicht dorthin verzweigen). (Eine Beschreibung der Unix-Varianten und ihrer Konfiguration finden Sie im verlinkten Artikel.) Einige Prozessorarchitekturen verfügen außerdem über separate Caches für Code und Daten , sodass Sie möglicherweise eine Anweisung zum Leeren des Cache ausgeben müssen. Alles in allem ist dies nicht etwas, was Sie von Hand tun möchten.

Es gibt eine Standard-Unix-API zum Laden und Ausführen von Code, die alles tut, um den geladenen Code ausführbar zu machen: dlopen . Der Code muss aus einer Datei geladen werden.

Just-in-Time-Compiler versuchen normalerweise, schnellere Schnittstellen als dlopen zu finden. Sie müssen die stark plattformabhängigen Methoden zur Sicherstellung der Ausführbarkeit von Code verwalten.

EDIT: Danke an Bruce Ediger , der mich daran erinnert hat, dass der Cache geleert werden muss.


4

Auf einigen Hardwarekomponenten (wie den HP-PA-CPUs von HP) ist dies weitaus schwieriger, und auf anderen (wie der DEC Alpha-CPU) müssen Sie zuerst einen Befehls-Cache leeren, aber im Allgemeinen können Sie Code auf dem Heap ausführen. Das Folgende ist ein einigermaßen anständiges C-Sprachprogramm, das Code "auf dem Heap" ausführt.

#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
Ich habe dies versucht, aber ich verstehe nicht, warum ich einen Segmentierungsfehler für alle Werte außer 1 bekomme.
Sen

Ein Argument von 1 führt nur die Funktion aus - obwohl die Funktion eine Kopie von sich selbst auf dem Heap erstellt, führt sie die Heap-Kopie nicht aus. Ich weiß nicht, warum Sie eine Segmentierungsverletzung erhalten. Können Sie weitere Details zu Compiler, CPU, Betriebssystem usw. usw. angeben?
Bruce Ediger
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.