Interested Article - Самомодифицирующийся код

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

По времени проведения модификации метод делится на:

  • Модификация при инициализации — проводится один раз перед запуском изменяемого кода
  • Модификация на лету ( on-the-fly ) — изменение состояния программы во время исполнения

В обоих случаях изменение проходит непосредственно в машинном коде, когда новые инструкции перезаписывают старые (напр. условный переход , , , и т.п. заменяются на безусловный переход или NOP ). В наборе инструкций IBM/360 и Z/Architecture имеется инструкция EXECUTE (EX) , которая перезаписывает целевую инструкцию (записанную во втором байте команды EX) самыми младшими 8 битами регистра 1. На указанных архитектурах с её помощью реализуется стандартный, законный метод временного изменения инструкций.

Назначение

Основные применения самомодифицирующегося кода:

  • В критичных к безопасности местах для усложнения исследования кода ( полиморфные вирусы , некоторые типы защиты от копирования , упаковщики и т. д.).
  • В критичных к скорости местах для ускорения работы. Так, например, во время исполнения можно уменьшить длину критического пути исполнения. Вместо установки и последующей многократной проверкой флагов с условными переходами можно всего лишь изменить адрес и тип перехода в машинном коде. Многие порты движка Doom устанавливали прямо в машинном коде ширину экрана, это ускоряло отрисовку столбца .
  • Иногда используется для включения/отключения во время исполнения некоторой функциональности для тестирования или отладки. Так, в ОС Linux и Solaris при использовании отладочных инструментов и DTrace в некоторые места кода ядра или программ вставляются последовательности инструкций nop . При включении инструмента некоторые из этих последовательностей заменяются на безусловный переход на процедуру отладки. Использование СМК позволяет расставить значительное количество точек, в которых возможна отладка, слабо при этом влияя на скорость исполнения с отключенной отладкой.
  • В ядре Linux и, возможно, других ОС, используются для отключения частей ядра, не нужных в данном окружении. При загрузке Linux определяет, исполняется ли он на SMP или на однопроцессорной машине. Во втором случае часть примитивов синхронизации удаляется из кода ядра.
  • Чтобы машинный код можно было набрать в текстовом поле — см. EICAR . Функция выхода из программы (int 20) имеет вид CD 20 и текстом надёжно не набирается — для этого её поместили в тестовый «вирус» в зашифрованном виде и расшифровали на месте.

Применимость к процессорам с гарвардской архитектурой

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

Также, некоторые идеи гарвардской архитектуры реализуются в ОС (например, Data Execution Prevention в ОС Windows, в OpenBSD ) и в процессорах (для x86 — бит NX и подобные). В этих реализациях отдельные фрагменты памяти могут быть помечены как неисполняемые (то есть данные) или как исполняемые, но немодифицируемые (то есть код без права на изменение). Использование самомодифицирующегося кода в таких программных окружениях усложняется, так как его приходится располагать либо в незащищенной области памяти (иногда такой областью является стэк ), либо явно отключать защиту для подлежащего изменению кода.

Использование

  • JIT (Just in time — компиляция)
  • Динамическая трансляция
  • — при которой двоичный транслятор следит за частотой исполнения региона, и, если регион выполняется часто, проводится рекомпиляция этого региона с изменением его кода во время исполнения. В наиболее совершенных двоичных трансляторах может иметься до 4-5 последовательных уровней оптимизации региона.

Интерпретируемые языки

Языки Perl , PHP и Python позволяют программе создавать новый код во время выполнения и выполнять его, используя функцию eval, но не позволяют самомодифицироваться существующему коду (interactive python shell):

>>> x = 1
>>> eval('x + 1')
2
>>> eval('x')
1

Иллюзия модификации (при том, что никакой машинный код в действительности не изменяется) достигается путём изменения указателя функции, как в этом JavaScript -примере:

var f = function (x) {return x + 1};
alert(f(0)); //1

f = new Function('x', 'return x + 2'); // assign a new definition to f
alert(f(0)); //2

См. также

Примечания

  1. См., например, исходный код , функция ASM_PatchRowBytes .
  2. Касперски, абзац с "Процессоры семейства Pentium .."

Ссылки

Источник —

Same as Самомодифицирующийся код