Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #82, ОКТЯБРЬ 2005 г.

Ступени полиморфизма

Крис Касперски ака мыщъх

Хакер, номер #082, стр. 082-120-1


Вникаем в суть работы полиморфных генераторов

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

Существуют тысячи готовых полиморфных движков, вобравших в себя множество блестящих идей, и чтобы переплюнуть их, придется очень сильно постараться. Конечно, профессиональный хакер может (и должен!) бросить вызов своим коллегам, удавить мир очередным шедевром (или сначала удивить, а потом удавить). Но для этого требуется опыт. А где его взять? Правильно, начать программировать простенькие полиморфные генераторы, повторяя кем-то давно пройденный путь. И вот так маленькими шажками, хакерское сообщество движется путем прогресса из золотого века во тьму.

[классификация полиморфных генераторов]

Принято выделять шесть ступеней полиморфизма - от простейших «одноклеточных», до развитых организмов. Будем следовать этой классификации и мы. Рассмотрим как она выглядит с точки зрения хакера и антивируса.

[ступень 0: permutation или простейшие перестановки]

Идея: обрабатываемый код делится на блоки постоянного или переменного размера, которые в каждом поколении вируса переставляются в случайном порядке. Это еще не настоящий полиморфизм, но и обычным такой код уже не назовешь. Он легко программируется, но и легко обнаруживается. Содержимое блоков остается неизменным, поэтому с ними справляется даже сигнатурный поиск.

Поскольку блоки «нарезаются» еще на стадии проектирования, проблемы случайного «расщепления» машинных команд границами блоков не возникает, и сочинять собственный дизассемблер длин нам не приходится. Тем не менее, при программировании возникают следующие проблемы: поскольку, адреса блоков в каждом поколении меняются, машинный код должен быть полностью перемещаемым, то есть сохранять работоспособность независимо от своего местоположения. Это достигается путем отказа от непосредственных межблочных вызовов. Совершать переходы, вызывать функции, обращаться к переменным можно только в пределах «своего» блока. В практическом плане это значит, что вместе с кодом каждый блок несет и свои переменные.

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

[таблица косвенных вызовов с базовыми адресами всех блоков]

base_table:

block_1 DD offset block_1

block_2 DD offset block_2

...

block_N DD offset block_N

А вызов блока может выглядеть так:

[косвенный межблочный вызов, номер блока передается в регистре ESI, а смещение функции от начала блока - в регистре EBX, аргументы функции можно передавать через стек]

SHR ESI, 2 ; умножаем номер блока на 4

ADD ESI, offset base_table + 4

; переводим в смещение

Содержание  Вперед на стр. 082-120-2