Может ли программа на C изменить свой исполняемый файл? - PullRequest
21 голосов
/ 10 октября 2010

У меня было слишком много времени, и я начал задумываться, могу ли я написать самоизменяющуюся программу. С этой целью я написал «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» и кое-что для его изменения), и это работает нормально.

РЕДАКТИРОВАТЬ: я понимаю, что когда программа запускается, она полностью загружается в оперативную память. Таким образом, исполняемый файл на жестком диске является для всех целей и целей копией. Почему для него было бы проблемой изменить себя?

Есть ли обходной путь?

Спасибо

Ответы [ 9 ]

31 голосов
/ 10 октября 2010

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

Когда файл отображается таким образом, другое приложение (включаясам) не может записать в тот же файл, чтобы изменить его во время работы.(Кроме того, в Windows исполняемый исполняемый файл также нельзя переименовать, но в Linux и других системах Unix с файловыми системами на основе inode это можно сделать.)Вы делаете это, ОС делает это, используя семантику «копирование при записи», что означает, что базовый файл не изменен на диске, а копия страниц в памяти сделана с вашими изменениями.Однако, прежде чем получить возможность сделать это, вам обычно приходится играть с защитными битами в рассматриваемой памяти (например, VirtualProtect).

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

6 голосов
/ 23 ноября 2010

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

Пошаговый пример:

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

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

4 голосов
/ 10 октября 2010

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

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

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

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

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

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

4 голосов
/ 10 октября 2010

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

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

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

2 голосов
/ 24 января 2011

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

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

  1. создать новый пустой селектор («высокая» часть дальних указателей)
  2. установить его атрибуты для атрибутаXO area
  3. свойства доступа нового селектора должны быть установлены RO DATA, если вы только хотите посмотреть, что в нем
  4. , если вы хотите изменить данные, свойства доступа должны быть установлены в RW DATA

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

Windows сделала доступными строительные блоки для этого, начиная с Win16.Они, вероятно, все еще на месте.Я думаю, что Microsoft называет этот класс манипулирования указателями «громоздким».


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

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

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

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

1 голос
/ 10 октября 2010

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

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

РЕДАКТИРОВАТЬ: И не забывайте об основных причинах использования самоизменяющегося кода:

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

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

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

1 голос
/ 10 октября 2010

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

0 голосов
/ 10 октября 2010

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

0 голосов
/ 10 октября 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...