Самомодифицирующийся код | это... Что такое Самомодифицирующийся код? (original) (raw)
В этой статье не хватает ссылок на источники информации. Информация должна быть проверяема, иначе она может быть поставлена под сомнение и удалена.Вы можете отредактировать эту статью, добавив ссылки на авторитетные источники.Эта отметка установлена 14 мая 2011. |
---|
Самомодифицирующийся код (СМК) — программный приём, при котором приложение создаёт или изменяет часть своего программного кода во время выполнения. Такой код обычно применяют в программах, написанных под процессор с фон-неймановской организацией памяти.
По времени проведения модификации метод делится на
- Модификация при инициализации — основанная на входных параметрах программы, проводится один раз, перед запуском изменяемого кода.
- Модификация во время исполнения (on-the-fly) — основывается на конкретных состояниях программы, которые наблюдались при исполнении.
В обоих случаях изменение проходит непосредственно в машинном коде, когда новые инструкции перезаписывают старые (например, сравнение и переход заменяются на безусловный переход или NOP). В наборе инструкций IBM/360 и Z/Architecture имеется инструкция EXECUTE (EX), которая перезаписывает целевую инструкцию (записанную во втором байте команды EX) самыми младшими 8 битами регистра 1. На указанных архитектурах с её помощью реализуется стандартный, законный метод временного изменения инструкций.
Содержание
- 1 Назначение
- 2 Применимость к процессорам с Гарвардской архитектурой
- 3 Использование
- 4 См. также
- 5 Примечания
- 6 Ссылки
Назначение
Основные применения самомодифицирующегося кода:
- В критичных к безопасности местах для усложнения исследования кода (полиморфные вирусы, некоторые типы защиты от копирования, упаковщики и т. д.).
- В критичных к скорости местах для ускорения работы. Так, например, во время исполнения можно уменьшить длину критического пути исполнения. Вместо установки и последующей многократной проверкой флагов с условными переходами, можно всего лишь изменить адрес и тип перехода в машинном коде. Многие порты движка Doom устанавливали прямо в машинном коде ширину экрана, это ускоряло отрисовку столбца[1].
- Иногда используется для включения/отключения во время исполнения некоторой функциональности для тестирования или отладки. Так, в ОС Linux и Solaris при использовании отладочных инструментов Kprobes и DTrace в некоторые места кода ядра или программ вставляются последовательности инструкций nop. При включении инструмента некоторые из этих последовательностей заменяются на безусловный переход на процедуру отладки. Использование СМК позволяет расставить значительное количество точек, в которых возможна отладка, слабо при этом влияя на скорость исполнения с отключенной отладкой.
- В ядре Linux и, возможно, других ОС, используются для отключения частей ядра, ненужных в данном окружении. При загрузке Linux определяет, исполняется ли он на SMP или на однопроцессорной машине. Во втором случае часть примитивов синхронизации удаляется из кода ядра.
Применимость к процессорам с Гарвардской архитектурой
В Гарвардской архитектуре память для кода и память для данных разделены. Соответственно, в них сильно усложняется работа самомодифицирующегося кода. Хотя архитектура x86 определена как фон-неймановская (с единой памятью кода и данных), большинство современных процессоров имеют раздельные области кэша для кода и для данных. При этом кэш кода не поддерживает запись, и при изменении закэшированного участка памяти может потребоваться либо аппаратно проведенный частичный или полный сброс кэша кода (x86) либо явная инструкция процессору на сброс кэша кода (sparc). Из-за этого только что измененный код может исполняться медленнее, либо потребовать дополнительных команд для правильной работы. Также изменение кода сбрасывает конвейер процессора.[2]
Также, некоторые идеи Гарвардской архитектуры реализуются в ОС (например, Data Execution Prevention в ОС Windows, W^X в OpenBSD) и в процессорах (для x86 — бит NX и подобные). В этих реализациях отдельные фрагменты памяти могут быть помечены как неисполняемые (то есть данные) или как исполняемые но немодифицируемые (то есть код без права на изменение). Использование самомодифицирующегося кода в таких программных окружениях усложняется, так как его приходится располагать либо в незащищенной области памяти (иногда такой областью является стэк), либо явно отключать защиту для подлежащего изменению кода.
Использование
- JIT (Just in time — компиляция)
- Динамическая трансляция
- Динамическая рекомпиляция — при которой Двоичный транслятор следит за частотой исполнения региона, и, если регион выполняется часто, проводится рекомпиляция этого региона с изменением его кода во время исполнения. В наиболее совершенных двоичных трансляторах может иметься до 4-5 последовательных уровней оптимизации региона.
См. также
Примечания
- ↑ См., например, исходный код Doom Legacy, функция
ASM_PatchRowBytes
. - ↑ Касперски, абзац с "Процессоры семейства Pentium .."