Вид самомодифицирующейся программы на C

Можно ли написать функцию C, которая выполняет следующее?

  1. Выделить кучу памяти в куче
  2. Пишет в него машинный код
  3. Выполняет инструкции этих машин

Конечно, мне пришлось бы вручную восстановить состояние стека до того, каким оно было до выполнения этих машинных инструкций, но я хочу знать, возможно ли это в первую очередь.

Может, вопрос стоит закрыть как дубликат?

Norman Ramsey 03.01.2009 07:44

См. stackoverflow.com/questions/397064/…

Eclipse 03.01.2009 08:16
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
594
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Это очень похоже на этот вопрос :)

Прочтите вызывающий код, хранящийся в куче, из 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
}

Это будет работать? Ну может в зависимости от железа и ОС. Большинство современных ОС защищают кучу (через отображение памяти или подобное) от ПК, перемещающегося в нее. Это полезная вещь для целей безопасности, потому что мы могли бы точно так же нет позволить вам получить такой полный контроль.

Другие вопросы по теме