TL; DR Скомпилировать с -z execstack
В программе происходит сбой, поскольку символ buffer
входит в секцию .data
, которая по очереди идет вместе сдругие разделы, в сегмент ELF, сопоставленные для чтения-записи, но не исполняемые .
Чтобы сделать исполняемый файл buffer
лучшим вариантом, было бы создать новый сегмент ELF с флагами RWE и назначить ему новый раздел, а затем указать GCC поместить buffer
в этот новый раздел.
Это можно сделать, в принципе с помощью следующего сценария компоновщика:
PHDRS
{
MYSEG PT_LOAD FLAGS (7);
}
SECTIONS
{
MYSECT : { *(MYSECT) } : MYSEG
}
, с изменением источника:
#include <stdio.h>
char buffer[] __attribute__ ((section ("MYSECT"))) ={0x90,0x90,0xC3};
int main(int argc, char *argv[])
{
void (*fct)();
fct=buffer;
fct();
return 0;
}
и компиляцией, передающей -T
переключиться на GCC.
Но это не будет работать.
GCC использует скрипт компоновщика по умолчанию на основе командной строки, а переключатель -T
полностью его заменяет.
Возможно получитьскрипт, используемый GCC с -Wl,-verbose
и обновляющий его.
Если мы разделим компиляцию и компоновку, сначала вызвав GCC с -c
, а затем LD, мы получим только один сегмент, потому что это то, что мы вставили в скрипт компоновщика - тем самым потерпев поражение во всех наших усилиях по созданию только buffer
единственные исполняемые данные.
С -z execstack
мы фактически только говорим GCC использовать скрипт компоновщика, который устанавливает GNU_STACK
ELF-сегмент RWE.
Это сегмент маркера (размер и lma равны нулю), используемыезагрузчик для установки правильных разрешений для страниц стека.
Но на самом деле он используется в качестве переключателя совместимости - когда стек устанавливается как исполняемый, загрузчик устанавливает все записываемые страницы как исполняемые.
Если вы играете с шелл-кодами, -z execstack
сделает это легко, однако это подвергает ваше приложение множеству атак, но я думаю, это то, что вам нужно в первую очередь.