Есть ли какие-либо умные случаи изменения кода времени выполнения?

Можете ли вы придумать какие-либо законные (умные) приложения для изменения кода времени исполнения (программа, модифицирующая его собственный код во время выполнения)?

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

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

Ответ 1

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

  • Некоторые виртуальные машины используют компиляцию JIT для повышения производительности.
  • Создание специализированных функций на лету уже давно распространено в компьютерной графике. См. Роб Пайк и Барт Локанти и Джон Райзер Компоновка аппаратного обеспечения для растровой графики на Blit (1984) или этой публикации (2006) от Криса Лэттнера по использованию Apple LLVM для специализации кода времени выполнения в стеке OpenGL.
  • В некоторых случаях программное обеспечение использует метод, известный как трамплин, который включает динамическое создание кода в стеке (или другое место). Примерами являются GCC вложенные функции и механизм сигнала некоторых Unices.

Иногда код транслируется в код во время выполнения (это называется динамический двоичный перевод):

  • Эмуляторы, такие как Apple Rosetta, используют эту технику для ускорения эмуляции. Другим примером является Transmeta программное обеспечение для преобразования кода.
  • Сложные отладчики и профилировщики, такие как Valgrind или Pin используют его для инструмента ваш код во время его выполнения.
  • До того, как были добавлены расширения в набор инструкций x86, программное обеспечение для виртуализации, такое как VMWare, не могло напрямую запускать привилегированный код x86 внутри виртуальных машин. Вместо этого он должен переводить любые проблемные инструкции "на лету" в более подходящий пользовательский код.

Модификация кода может использоваться для ограничения ограничений набора команд:

  • Было время (давно, я знаю), когда у компьютеров не было никаких инструкций по возврату из подпрограммы или косвенному адресу памяти. Самомодифицирующий код был единственным способом реализовать подпрограммы, указатели и массивы.

Больше случаев модификации кода:

  • Многие отладчики заменяют инструкции на выполнять контрольные точки.
  • Некоторые динамические компоновщики изменяют код во время выполнения. В этой статье приведены некоторые сведения о перемещении DLL в Windows во время выполнения, что фактически является формой изменения кода.

Ответ 2

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

Ответ 3

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

Ответ 4

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

Ответ 5

Существует много случаев:

  • Вирусы обычно используют самомодифицирующийся код для "деобфускации" своего кода перед исполнением, но этот метод также может быть полезен при расстройстве обратной инженерии, взломах и нежелательных хакерах.
  • В некоторых случаях во время выполнения может быть определенная точка (например, сразу после чтения конфигурационного файла), когда известно, что - для остальной части жизненного цикла процесса - конкретная ветка всегда или никогда не будет приниматься: вместо того, чтобы излишне проверять некоторую переменную, чтобы определить, к какому пути к ветки, сама инструкция ветвления может быть изменена соответствующим образом
    • например. Стало известно, что будет обрабатываться только один из возможных производных типов, так что виртуальная отправка может быть заменена конкретным вызовом
    • После обнаружения того, какое оборудование доступно, использование соответствующего кода может быть жестко запрограммировано
  • Ненужный код может быть заменен инструкциями no-op или перепрыгивать через него или иметь следующий бит кода, сдвинутый прямо на место (проще, если использовать коды, не зависящие от позиции)
  • Код, написанный для облегчения его собственной отладки, может ввести команду trap/signal/interrupt, ожидаемую отладчиком в стратегическом месте.
  • Некоторые предикатные выражения, основанные на вводе пользователя, могут быть скомпилированы в собственный код с помощью библиотеки
  • Вложение некоторых простых операций, которые не отображаются до выполнения (например, из динамически загруженной библиотеки)...
  • Условно добавление шагов самоинструментации/профилирования
  • Трещины могут быть реализованы как библиотеки, которые изменяют код, который их загружает (а не "самостоятельно", точно изменяя, но нуждаются в тех же методах и разрешениях).
  • ...

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

Из Википедии:

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

В таких ОС даже для таких программ, как Java VM, необходимы права root/admin для выполнения их JIT-кода. (Подробнее см. http://en.wikipedia.org/wiki/W%5EX)

Ответ 6

Некоторые компиляторы использовали его для статической инициализации переменных, избегая стоимости условного для последующих обращений. Другими словами, они реализуют "исполнять этот код только один раз", перезаписывая этот код с помощью no-ops при первом запуске.

Ответ 7

Синтез ОС в основном частично оценил вашу программу в отношении вызовов API и заменил код ОС на результаты. Главное преимущество заключается в том, что многие проверки ошибок ушли (потому что если ваша программа не собирается просить ОС делать что-то глупое, ее не нужно проверять).

Да, это пример оптимизации времени выполнения.

Ответ 8

Много лет назад я потратил утро на попытку отладки некоторого самомодифицирующего кода, одна команда изменила целевой адрес следующей команды, то есть я вычислил адрес ветки. Это было написано на языке ассемблера и отлично работало, когда я перешагнул программу по одной инструкции за раз. Но когда я запускал программу, она не удалась. В конце концов, я понял, что машина извлекает 2 инструкции из памяти и (по мере того, как инструкции были изложены в памяти), команда, которую я модифицировал, уже была извлечена, и, таким образом, машина выполняла немодифицированную (неправильную) версию инструкции. Конечно, когда я отлаживал, он выполнял только одну инструкцию за раз.

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

(Примечание - я не включил JIT в категорию самомодифицирующего кода. JIT переводит из одного представления кода в альтернативное представление, он не изменяет код)

В целом, это всего лишь плохая идея - действительно опрятная, действительно неясная, но очень плохая.

конечно - если все, что у вас есть, это 8080 и ~ 512 байт памяти, вам, возможно, придется прибегать к подобным практикам.

Ответ 9

С точки зрения ядра операционной системы каждый компилятор Just In Time и компоновщик Runtime выполняет само модификацию текста программы. Ярким примером может служить интерпретатор Google V8 ECMA Script.

Ответ 10

Вы знаете старый каштан, что нет логической разницы между аппаратным и программным обеспечением... можно также сказать, что между кодом и данными нет логической разницы.

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

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

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

Ответ 11

Еще одна причина самомодифицирующего кода (на самом деле "самогенерирующий" код) заключается в реализации механизма компиляции Just-In-time для производительности. Например. программа, которая читает алгебраическое выражение и вычисляет его по диапазону входных параметров, может преобразовать выражение в машинный код, прежде чем указывать расчет.

Ответ 12

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

Ответ 13

В Linux Kernel есть загружаемые модули ядра, которые делают именно это.

Emacs также обладает этой способностью, и я использую его все время.

Все, что поддерживает динамическую архитектуру плагина, существенно изменяет его код во время выполнения.

Ответ 14

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

Ответ 15

Одним из вариантов использования является тестовый файл EICAR, который используется для тестирования антивирусных программ.

X5O! P% @AP [4\PZX54 (P ^) 7CC) 7} $EICAR-STANDARD-ANTIVIRUS-TEST-FILE! $H + H *

Он должен использовать модификацию собственного кода, потому что исполняемый файл должен содержать только печатные/типизируемые символы ASCII в диапазоне [21h-60h, 7Bh-7Dh], которые невозможно было бы закодировать некоторые необходимые инструкции

Подробности объясняются здесь

Ответ 16

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

1) он ищет существующую базу кода для аналогичного алгоритма

2), если в базе кода нет аналогичного алгоритма, программа просто добавляет новый алгоритм

3), если существует аналогичный алгоритм, программа (возможно, с некоторой помощью от пользователя) изменяет существующий алгоритм, чтобы иметь возможность использовать как старую цель, так и новую цель

Есть вопрос, как это сделать в Java: Каковы возможности самомодификации кода Java?

Ответ 17

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

Я ни в коем случае не специалист, но попросите одного из парней Lisp говорить об этом! Есть причина что они говорят, что Lisp - самый мощный язык вокруг, а умные люди - нет, что они, вероятно, правы.