Interested Article - Самомодифицирующийся код
- 2020-12-15
- 1
Самомодифицирующийся код (СМК) — программный приём, при котором приложение создаёт или изменяет часть своего программного кода во время выполнения. Такой код обычно применяют в программах, написанных под процессор с фон-неймановской организацией памяти.
По времени проведения модификации метод делится на:
- Модификация при инициализации — проводится один раз перед запуском изменяемого кода
- Модификация на лету ( 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
См. также
- Метапрограммирование
- Monkey patch — динамическая подмена исполняемых процедур программы во время исполнения без изменения исходного кода.
Примечания
-
См., например, исходный код
, функция
ASM_PatchRowBytes
. - Касперски, абзац с "Процессоры семейства Pentium .."
Ссылки
- 2020-12-15
- 1