Можно ли написать функцию C, которая выполняет следующее?
Конечно, мне пришлось бы вручную восстановить состояние стека до того, каким оно было до выполнения этих машинных инструкций, но я хочу знать, возможно ли это в первую очередь.
См. stackoverflow.com/questions/397064/…





Это очень похоже на этот вопрос :)
Прочтите вызывающий код, хранящийся в куче, из vC++. На posix подходит mprotect (посмотрите man mprotect):
char *mem = malloc(sizeof(code));
mprotect(mem, sizeof(code), PROT_READ|PROT_WRITE|PROT_EXEC);
memcpy(mem, code, sizeof(code));
// now arrange some code to jump to mem. But read the notes here on casting
// from void* to a function pointer:
// http://www.opengroup.org/onlinepubs/009695399/functions/dlsym.html
Однако там говорится:
Whether PROT_EXEC has any effect different from PROT_READ is architecture- and kernel version-dependent. On some hardware architectures (e.g., i386), PROT_WRITE implies PROT_READ.
Так что лучше сначала проверьте, работает ли это в вашей операционной системе.
RE: восстановление стека вручную
Если вы следуете соглашениям о вызовах, используемым вашей платформой / компилятором внутри генерируемого вами машинного кода, вам не придется выполнять какое-либо восстановление стека вручную. Компилятор сделает это за вас, когда вы сделаете
* pfunc (аргументы)
он должен добавить любые необходимые шаги до или после обработки стека вызовов.
Однако просто убедитесь, что вы следуете правильным соглашениям внутри сгенерированного кода.
Это конечно возможный. По разным причинам мы потратили много усилий последние 30-40 лет на то, чтобы сделать это как можно сложнее, но это возможно. В настоящее время в большинстве систем существуют аппаратные и программные механизмы, которые пытаются защитить пространство данных от выполнения.
Однако основы довольно просты: вы создаете фрагмент кода и собираете его вручную или с помощью компилятора. Затем вам понадобится фрагмент кода, поэтому вы вставляете код в свою программу.
unsigned int prgm[] = { 0x0F, 0xAB, 0x9A ... }; // Random numbers, just as an example
поскольку вы хотели использовать куча, вам необходимо выделить пространство
void * myspace ;
if ((myspace= malloc(sizeof(prgm))) != NULL) {
memcpy(myspace, pgrm, sizeof(pgrm));
} else { // allocation error
}
Теперь вам нужен способ заставить счетчик команд указывать на этот фрагмент данных, который также является вашим фрагментом кода. Вот где вам нужно немного лукавства. Установка счетчика программ не составляет большого труда; это просто инструкция JUMP для вашей базовой машины. Но как это сделать?
Один из самых простых способов - целенаправленно возиться со стеком. Стек, опять же концептуально, выглядит примерно так (детали зависят как от вашей пары ОС и компилятора, так и от вашего оборудования):
| subroutine return addr |
| parameters ... |
| automatic variables |
Основная хитрость заключается в том, чтобы незаметно ввести адрес вашего кода в обратный адрес; когда процедура возвращается, она в основном переходит к этому адресу возврата. Если вам удастся подделать это, компьютер будет настроен на то, что вам нужно.
Итак, вам нужна процедура, назовем ее "goThere ()"
void goThere(void * addr){
int a ; // observe above; this is the first space
// on the stack following the parameters
int * pa; // so we use it's address
pa = (&a - (sizeof(int)+(2*sizeof(void*))) ; // so use the address
// but back up by the size of an int, the pointer on the
// stack, and the return address
// Now 'pa' points to the routine's return add on the stack.
*pa = addr; // sneak the address of the new code into return addr
return ; // and return, tricking it into "returning"
// to the address of your special code block
}
Это будет работать? Ну может в зависимости от железа и ОС. Большинство современных ОС защищают кучу (через отображение памяти или подобное) от ПК, перемещающегося в нее. Это полезная вещь для целей безопасности, потому что мы могли бы точно так же нет позволить вам получить такой полный контроль.
Может, вопрос стоит закрыть как дубликат?