Câu trả lời:
Có lẽ. Nếu heap có thể thực thi được, bạn có thể phân nhánh tới mã đó. Nhưng một số biến thể unix làm cho không gian heap không thể thực thi được , do đó để khai thác một số lỗ hổng bảo mật như bộ đệm tràn ra khó khăn hơn (ngay cả khi bạn có thể tiêm mã vào chương trình, bạn có thể không thể phân nhánh vào nó). (Xem bài viết được liên kết để thảo luận về các biến thể unix và cấu hình của chúng.) Ngoài ra, một số kiến trúc bộ xử lý có bộ đệm riêng cho mã và dữ liệu , do đó bạn có thể cần phải đưa ra lệnh xóa bộ đệm. Nói chung, đây không phải là thứ bạn muốn làm bằng tay.
Có một API unix tiêu chuẩn để tải và thực thi mã, nó sẽ làm những gì cần thiết để làm cho mã được tải thực thi: dlopen . Mã phải được tải từ một tập tin.
Trình biên dịch đúng lúc thường cố gắng tìm các giao diện nhanh hơn dlopen. Họ phải quản lý các cách phụ thuộc nhiều vào nền tảng để đảm bảo khả năng thực thi mã.
EDIT: Cảm ơn Bruce Ediger đã nhắc nhở tôi về sự cần thiết phải xóa bộ đệm.
Trên một số phần cứng (như CPU HP-PA của HP) khó khăn hơn rất nhiều và trên các phần cứng khác (như CPU DEC Alpha) trước tiên bạn phải thực hiện xóa bộ đệm ẩn lệnh, nhưng nói chung, bạn có thể thực thi mã trên heap. Sau đây là một chương trình ngôn ngữ C khá hợp lý thực thi mã "trên heap".
#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;}