Если ваш вопрос о том, как перейти к фрагменту кода на определенном языке программирования, задайте вопрос о переполнении стека .
Жиль "ТАК - перестань быть злым"
Ответы:
5
Может быть. Если куча исполняемая, вы можете перейти к этому коду. Но некоторые варианты Unix делают пространство кучи неисполнимым , чтобы сделать использование некоторых уязвимостей безопасности, таких как переполнение буфера, более трудным (тогда, даже если вы можете внедрить код в программу, вы не сможете перейти к нему). (См. Связанную статью для обсуждения вариантов Unix и их конфигурации.) Также некоторые архитектуры процессоров имеют отдельные кэши для кода и данных , поэтому вам может потребоваться выполнить инструкцию очистки кэша. В общем, это не то, что вы хотите сделать вручную.
Существует стандартный API Unix для загрузки и выполнения кода, который сделает все возможное, чтобы сделать загружаемый код исполняемым: dlopen . Код должен быть загружен из файла.
Компиляторы точно вовремя обычно пытаются найти более быстрые интерфейсы, чем dlopen. Они должны управлять сильно зависящими от платформы способами обеспечения выполнения кода.
РЕДАКТИРОВАТЬ: Спасибо Брюс Эдигер за напоминание о необходимости очистить кэш.
На некоторых аппаратных средствах (таких как процессоры HP HP-PA) это гораздо сложнее, а на других (например, DEC Alpha CPU) сначала необходимо выполнить очистку кэша команд, но, в общем, вы можете выполнять код в куче. Ниже приводится достаточно приличная программа на языке Си, которая выполняет код «в куче».
#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.
Сен
Аргумент 1 просто выполняет функцию - хотя функция создает свою копию в куче, она не выполняет копию кучи. Я не знаю, почему вы получаете нарушение сегментации. Можете ли вы рассказать подробнее о компиляторе, процессоре, ОС и т. Д.?
Ответы:
Может быть. Если куча исполняемая, вы можете перейти к этому коду. Но некоторые варианты Unix делают пространство кучи неисполнимым , чтобы сделать использование некоторых уязвимостей безопасности, таких как переполнение буфера, более трудным (тогда, даже если вы можете внедрить код в программу, вы не сможете перейти к нему). (См. Связанную статью для обсуждения вариантов Unix и их конфигурации.) Также некоторые архитектуры процессоров имеют отдельные кэши для кода и данных , поэтому вам может потребоваться выполнить инструкцию очистки кэша. В общем, это не то, что вы хотите сделать вручную.
Существует стандартный API Unix для загрузки и выполнения кода, который сделает все возможное, чтобы сделать загружаемый код исполняемым: dlopen . Код должен быть загружен из файла.
Компиляторы точно вовремя обычно пытаются найти более быстрые интерфейсы, чем dlopen. Они должны управлять сильно зависящими от платформы способами обеспечения выполнения кода.
РЕДАКТИРОВАТЬ: Спасибо Брюс Эдигер за напоминание о необходимости очистить кэш.
источник
На некоторых аппаратных средствах (таких как процессоры HP HP-PA) это гораздо сложнее, а на других (например, DEC Alpha CPU) сначала необходимо выполнить очистку кэша команд, но, в общем, вы можете выполнять код в куче. Ниже приводится достаточно приличная программа на языке Си, которая выполняет код «в куче».
источник