Подтвердить что ты не робот

Может ли программа C изменить свой исполняемый файл?

У меня было слишком много времени на моих руках, и я начал задаваться вопросом, могу ли я написать программу самомодификации. С этой целью я написал "Hello World" в C, а затем использовал шестнадцатеричный редактор, чтобы найти местоположение строки "Hello World" в скомпилированном исполняемом файле. Можно ли изменить эту программу, чтобы открыть себя и перезаписать строку "Hello World"?

char* str = "Hello World\n";

int main(int argc, char* argv) {

  printf(str);

  FILE * file = fopen(argv, "r+");

  fseek(file, 0x1000, SEEK_SET);
  fputs("Goodbyewrld\n", file);      
  fclose(file);    

  return 0;
}

Это не работает, я предполагаю, что там что-то мешает ему открыться, так как я могу разделить его на две отдельные программы ( "Hello World" и что-то изменить), и он отлично работает.

EDIT: Я понимаю, что когда программа запускается, она полностью загружается в ram. Таким образом, исполняемый файл на жестком диске, по сути, является копией. Почему это было бы проблемой для его модификации?

Есть ли способ обхода?

Спасибо

4b9b3361

Ответ 1

В Windows при запуске программы весь файл *.exe отображается в память с помощью функций, отображаемых в памяти в Windows., Это означает, что файл не обязательно загружается сразу, но вместо этого страницы файла загружаются по требованию по мере их доступа.

Когда файл сопоставляется таким образом, другое приложение (включая его) не может записать в тот же файл, чтобы изменить его во время его запуска. (Кроме того, в Windows исполняемый исполняемый файл нельзя переименовать, но он может работать в Linux и других Unix-системах с файловыми системами на базе inode).

Можно изменить биты, отображаемые в память, но если вы это сделаете, ОС сделает это с использованием семантики "копирование на запись", что означает, что основной файл не изменяется на диске, а копия страницы в памяти сделаны с вашими изменениями. Прежде чем разрешить это сделать, вам обычно приходится сражаться с битами защиты в соответствующей памяти (например, VirtualProtect).

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

Ответ 2

Если вы используете Windows, вы можете сделать следующее:

Шаг за шагом Пример:

  • Вызовите VirtualProtect() на кодовых страницах, которые вы хотите изменить, с защитой PAGE_WRITECOPY.
  • Измените страницы кода.
  • Вызовите VirtualProtect() на измененных кодовых страницах с защитой PAGE_EXECUTE.
  • Вызов FlushInstructionCache().

Для получения дополнительной информации см. Как изменить исполняемый код в памяти (в архиве: август 2010 г.)

Ответ 3

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

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

В других системах вы даже не можете знать, где находится файл.

Ответ 4

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

Однако, если вы действительно хотите увидеть собственный самомодифицирующийся код в действии, у вас есть некоторые возможности:

  • Попробуйте микроконтроллеры, у более простых нет передовой конвейерной обработки. Самый дешевый и быстрый выбор, который я нашел, - MSP430 USB-Stick

  • Если эмуляция в порядке для вас, вы можете запустить эмулятор для более старой не-конвейерной платформы.

  • Если вам нужен самомодифицирующийся код только для удовольствия, вы можете получить еще больше удовольствия от саморазрушающего кода (точнее уничтожения врага) на Corewars.

  • Если вы хотите перейти от C, чтобы сказать диалект Lisp, код, который пишет код, очень естественен. Я бы предложил Scheme, который намеренно оставался небольшим.

Ответ 5

Если мы говорим об этом в среде x86, это не может быть невозможно. Его следует использовать с осторожностью, потому что инструкции x86 имеют переменную длину. Длинная инструкция может перезаписать следующую инструкцию (-ы), а более короткая - оставить остаточные данные из перезаписанной команды, которая должна быть указана.

Когда x86 сначала стала защищенной, справочные руководства Intel рекомендовали следующий метод для отладки доступа к областям XO (только для выполнения):

  • создайте новый пустой пульт ( "высокая" часть дальних указателей)
  • установить его атрибуты в атрибуты области XO
  • для свойств доступа к новому селектору необходимо установить RO DATA, если вы хотите посмотреть, что в нем
  • Если вы хотите изменить данные, свойства доступа должны быть установлены в RW DATA

Таким образом, ответ на проблему находится на последнем шаге. RW необходим, если вы хотите иметь возможность вставить инструкцию точки останова, что и делают отладчики. Более современные процессоры, чем 80286, имеют внутренние регистры отладки, позволяющие использовать функции неинтрузивного мониторинга, которые могут привести к выдаче контрольной точки.

Windows сделала доступными строительные блоки для этого, начиная с Win16. Вероятно, они все еще на месте. Я думаю, что Microsoft называет этот класс манипуляций с указателями "thunking".


Я написал очень быстрый 16-битный движок базы данных в PL/M-86 для DOS. Когда Windows 3.1 прибыла (работала на 80386), я поместил ее в среду Win16. Я хотел использовать 32-битную память, но не было никакого PL/M-32 (или Win32, если на то пошло).

чтобы решить проблему, которую моя программа использовала thunking следующим образом

  • определены 32-разрядные указатели (sel_16: offs_32) с использованием структур
  • выделенные 32-битные области данных (< = → 64 КБ) с использованием глобальной памяти и полученные в формате 16-разрядного дальнего указателя (sel_16: offs_16)
  • заполняет данные в структурах, копируя селектор, а затем вычисляет смещение с использованием 16-битного умножения с 32-битными результатами.
  • загрузил указатель/структуру в es: ebx, используя префикс переопределения размера команды
  • доступ к данным с использованием комбинации размера инструкции и префиксов размера операнда

Как только механизм был без ошибок, он работал без сбоев. Самые большие области памяти, которые использовала моя программа, - 2304 * 2304 двойная точность, которая составляет около 40 МБ. Даже сегодня я бы назвал это "большой" блок памяти. В 1995 году он составлял 30% от обычной палки SDRAM (128 МБ PC100).

Ответ 6

На многих платформах есть непереносимые способы сделать это. В Windows вы можете сделать это с помощью WriteProcessMemory(), например. Однако в 2010 году это обычно очень плохая идея. Это не те дни DOS, где вы кодируете в сборке и делаете это, чтобы сэкономить место. Это очень сложно, и вы в основном просите о стабильности и проблемах безопасности. Если вы не делаете что-то очень низкоуровневое, как отладчик, я бы сказал, не беспокойтесь об этом, проблемы, которые вы представите, не стоят того, что вы могли бы иметь.

Ответ 7

Самомодифицирующийся код используется для модификаций в памяти, а не в файле (например, распаковщики времени выполнения, такие как UPX do). Кроме того, файловое представление программы более сложно работать из-за относительных виртуальных адресов, возможных перемещений и модификаций заголовков, необходимых для большинства обновлений (например, путем изменения Hello world! на longer Hello World вам необходимо расширить сегмент данных в файле).

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

EDIT: И не забывайте об основных причинах использования самомодифицирующего кода:

1) Obfuscation, так что фактически выполненный код не является кодом, который вы увидите с простым статическим анализом файла.

2) Производительность, что-то вроде JIT.

Ни одно из них не является преимуществом при модификации исполняемого файла.

Ответ 8

Если вы работаете в Windows, я считаю, что он блокирует файл, чтобы предотвратить его изменение во время его запуска. Вот почему вам часто нужно выйти из программы, чтобы установить обновление. То же самое не относится к системе Linux.

Ответ 9

В новых версиях Windows CE (atleast 5.x newer), где приложения работают в пространстве пользователя (по сравнению с более ранними версиями, где все приложения работали в режиме супервизора), приложения даже не могли прочитать собственный исполняемый файл.